2012-02-23 13 views
6

estoy teniendo problemas para entender por qué el movimiento Foo constructor intenta invocar ~ptr en el siguiente ejemplo:¿Dónde se esconde el destructor en este código?

#include <utility> 

template <typename T, typename Policy> 
class ptr { 
    T * m_t; 
public: 
    ptr() noexcept : m_t(0) {} 
    explicit ptr(T *t) noexcept : m_t(t) {} 
    ptr(const ptr &other) noexcept : m_t(Policy::clone(other.m_t)) {} 
    ptr(ptr &&other) noexcept : m_t(other.m_t) { other.m_t = 0; } 
    ~ptr() noexcept { Policy::delete_(m_t); } 
    ptr &operator=(const ptr &other) noexcept 
    { ptr copy(other); swap(copy); return *this; } 
    ptr &operator=(ptr &&other) noexcept 
    { std::swap(m_t,other.m_t); return *this; } 

    void swap(ptr &other) noexcept { std::swap(m_t, other.m_t); } 

    const T * get() const noexcept { return m_t; } 
    T * get() noexcept { return m_t; } 
}; 

class FooPolicy; 
class FooPrivate; 
class Foo { 
    // some form of pImpl: 
    typedef ptr<FooPrivate,FooPolicy> DataPtr; 
    DataPtr d; 
public: 
    // copy semantics: out-of-line 
    Foo(); 
    Foo(const Foo &other); 
    Foo &operator=(const Foo &other); 
    ~Foo(); 

    // move semantics: inlined 
    Foo(Foo &&other) noexcept 
     : d(std::move(other.d)) {} // l.35 ERR: using FooDeleter in ~ptr required from here 
    Foo &operator=(Foo &&other) noexcept 
    { d.swap(other.d); return *this; } 
}; 

GCC 4.7:

foo.h: In instantiation of ‘ptr<T, Policy>::~ptr() [with T = FooPrivate; Policy = FooPolicy]’: 
foo.h:34:44: required from here 
foo.h:11:14: error: incomplete type ‘FooPolicy’ used in nested name specifier 

Clang 3,1-pre:

foo.h:11:14: error: incomplete type 'FooPolicy' named in nested name specifier 
    ~ptr() { Policy::delete_(m_t); } 
      ^~~~~~~~ 
foo.h:34:5: note: in instantiation of member function 'ptr<FooPrivate, FooPolicy>::~ptr' requested here 
    Foo(Foo &&other) : d(std::move(other.d)) {} 
    ^
foo.h:23:7: note: forward declaration of 'FooPolicy' 
class FooPolicy; 
    ^
foo.h:11:20: error: incomplete definition of type 'FooPolicy' 
    ~ptr() { Policy::delete_(m_t); } 
      ~~~~~~^~ 
2 errors generated. 

¿Qué está pasando? Estoy escribiendo constructores de movimientos para evitar ejecutar copiadores y controladores. Tenga en cuenta que este es un archivo de cabecera que intenta ocultar su implementación (idioma pimpl), por lo que FooDeleter un tipo completo no es una opción.

EDIT: Después de la respuesta de Bo, agregué noexcept donde pude (editado arriba). Pero los errores siguen siendo los mismos.

+2

mirando ese código por un segundo y tengo un mini dolor de cabeza instantáneo, gracias por la pregunta, tuve un problema similar recientemente afortunadamente no tuvo que seguir – lurscher

+1

No es una respuesta a su pregunta, pero como los mensajes del compilador señalan, ' Deleter (m_t); 'no significa lo que piensas que significa. Define una variable entre paréntesis redundante no utilizada 'm_t' de tipo' Deleter'. – hvd

+1

"Escribo constructores de movimientos para evitar ejecutar copiadores y dtors". Esa no es una buena razón para escribir un constructor de movimientos. Las razones para escribir constructores de movimientos se deben a que se trata de un tipo de contenedor de bajo nivel que necesita semántica de movimiento, o bien está usando Visual Studio que no genera implícitamente constructores de movimiento para usted. De lo contrario, deje que el compilador haga su trabajo. –

Respuesta

9

Crea un nuevo objeto Foo que contiene un miembro ptr<something>. En caso de que el constructor Foo falle, el compilador debe llamar a los destructores para cualquier miembro completamente construido del Foo parcialmente construido.

Pero no puede instanciar ~ptr<incomplete_type>(), por lo que falla.

Tiene un caso similar con un destructor privado. Eso también le impide crear objetos de ese tipo (a menos que lo haga un amigo o una función miembro).

+2

+1, y la solución en este caso podría ser tan simple como mover todas las definiciones de constructor y destructor al archivo de implementación donde se completan todos los tipos. –

+0

+1; En otras palabras, no puede definir cualquiera de los métodos de 'Foo' en línea porque está ocultando ** todos ** los detalles en el nivel del encabezado. Asumiendo los cambios de mi [comentario anterior] (http: // stackoverflow.com/questions/9417477/where-does-the-destructor-hide-in-this-code # comment12270235_9417477), puede usar 'Foo(): d (new FooPrivate) {} Foo (Foo const &) = default; Foo (Foo &&) = predeterminado; ~ Foo() = predeterminado; Foo & operator = (Foo) = predeterminado; 'como implementaciones, ya que' ptr' maneja adecuadamente todo. – CTMacUser

+0

@Bo: Su respuesta sonó lógica hasta que traté de enyesar todo 'ptr', así como también el operador' 'Foo' move ctor and assignment con' noexcept'. AFAIU, esa fue la razón original de 'noexcept': permitir que los ctors de movimiento se lancen. Sé que el mío no puede, entonces, ¿por qué GCC todavía invoca al dtor? Le dije que nada podría fallar, ¿no? –

Cuestiones relacionadas