2015-07-06 7 views
2

Я не понимаю следующий сценарий. Речь идет об использовании идиомы pimpl, основанной на std::unique_ptr в производном классе. Учитывая простую иерархию классов, указанную ниже:Pimpl с std :: unique_ptr в производном классе

class Foo 
{ 
public: 
    virtual ~Foo(); 
    //... 
}; 

struct X; 

class Bar : public Foo 
{ 
public: 
    ~Bar(); 
    //... 

private: 
    std::unique_ptr<X> _d; 
}; 

Я показываю только код, имеющий отношение к моему вопросу.

Представьте себе класс Foo, являющийся интерфейсом и классом «Бар», который его реализует. Я хочу использовать идиому pimpl в Bar. Деструкторы являются виртуальными и определены в соответствующих файлах cpp. Также полное определение struct X, которое объявлено только вперед, доступно в cpp, так что для деструктора Bar может быть создан экземпляр destructor std::unique_ptr<X>::~unique_ptr(). Когда я пытаюсь создать экземпляр Bar, я бы ожидать, что она работает

int main() 
{ 
    Bar b; 
} 

Вместо этого, я получаю ошибку компиляции использования неопределенного типа «X» с последующим сообщением не может удалить неполный тип (в обновлении Visual Studio 2013 2). Однако, если я явно добавлю конструктор по умолчанию к Bar, main() компилирует/строит правильно.

class Foo 
{ 
public: 
    virtual ~Foo(); 
}; 

struct X; 

class Bar : public Foo 
{ 
public: 
    Bar(); 
    ~Bar(); 

private: 
    std::unique_ptr<X> _d; 
}; 

Я не видеть соответствие между наличием конструкторов по умолчанию в этой иерархии классов и полнотой struct X в контексте std::unique_ptr<X> в Bar. Может ли кто-нибудь объяснить или указать на возможно уже существующее объяснение?

+2

Вам действительно нужен деструктор для Bar в файле, где X объявлен, только. –

+0

Боковое примечание, может быть, см. Это: https://stackoverflow.com/questions/24635255/is-it-possible-to-write-an-agile-pimpl-in-c/24638702#24638702 – Galik

+0

«переключатель» добавляет конструктор в 'Bar', добавив его в' Foo', ничего не меняет – WorldSEnder

ответ

5

Проблема заключается в том, что конструктор по умолчанию, добавленный в Bar, должен вызвать деструктор std::unique_ptr<X> в случае, если конструктор конструктора Bar выдает исключение.

Но std::unique_ptr<X>::~unique_ptr() называет детектора unique_ptr: std::default_delete<X> - для этого требуется X. [unique.ptr.single.dtor]

Когда вы объявляете конструктор самостоятельно, создание экземпляра не происходит, потому что конструктор объявляется и, таким образом, связывается в более поздний момент времени, и компилятор с радостью ест ваш код.

+0

«Но при создании экземпляра std :: unique_ptr компилятор также должен сгенерировать std :: unique_ptr :: ~ unique_ptr()» - это, по крайней мере, небрежно сформулировано. Неявное создание шаблонного класса не приводит к созданию всех его методов. Проблема в том, что деструктор Bar генерируется, когда остается область, в которой находится b. Это, в свою очередь, приводит к созданию уникального деструктора unique_ptr. Если «Бар b»; был заменен на намеренно просачивающийся «Bar * b = new Bar();», он будет компилироваться. (Не делайте этого, хотя.) –

+0

@ArneVogel, 'Bar * b = new Bar()' не компилируется: http://ideone.com/yFbE8V. Реальная проблема в том, что sizeof (X) статически утверждается, то? Метод не обязательно должен быть создан. – WorldSEnder

+0

@ArneVogel Я нашел настоящую причину, почему все это происходит. См. Обновленный ответ. – WorldSEnder

 Смежные вопросы

  • Нет связанных вопросов^_^