std :: unique_ptr с неполным типом не будет компилироваться

Я использую pimpl-idiom с std::unique_ptr :

 class window { window(const rectangle& rect); private: class window_impl; // defined elsewhere std::unique_ptr impl_; // won't compile }; 

Однако я получаю ошибку компиляции относительно использования неполного типа, в строке 304 в :

Недопустимое приложение ‘ sizeof ‘ для неполного типа ‘ uixx::window::window_impl

Насколько мне известно, std::unique_ptr должен быть использован с неполным типом. Это ошибка в libc ++ или я делаю что-то не так?

    Вот несколько примеров std::unique_ptr с неполными типами. Проблема заключается в разрушении.

    Если вы используете pimpl с unique_ptr, вам нужно объявить деструктор:

     class foo { class impl; std::unique_ptr impl_; public: foo(); // You may need a def. constructor to be defined elsewhere ~foo(); // Implement (with {}, or with = default;) where impl is complete }; 

    потому что иначе компилятор генерирует значение по умолчанию, и для этого требуется полное объявление foo::impl .

    Если у вас есть конструкторы шаблонов, то вы ввернуты, даже если вы не impl_ член impl_ :

     template  foo::foo(T bar) { // Here the compiler needs to know how to // destroy impl_ in case an exception is // thrown ! } 

    В области пространства имен использование unique_ptr также не будет работать:

     class impl; std::unique_ptr impl_; 

    поскольку компилятор должен знать здесь, как уничтожить этот объект статической продолжительности. Обходной путь:

     class impl; struct ptr_impl : std::unique_ptr { ~ptr_impl(); // Implement (empty body) elsewhere } impl_; 

    Как упоминал Александр С. , проблема сводится к тому, что деструктор window неявно определяется в тех местах, где тип window_impl еще не завершен. В дополнение к его решениям, еще одним обходным решением, которое я использовал, является объявление функтора Deleter в заголовке:

     // Foo.h class FooImpl; struct FooImplDeleter { void operator()(FooImpl *p); } class Foo { ... private: std::unique_ptr impl_; }; // Foo.cpp ... void FooImplDeleter::operator()(FooImpl *p) { delete p; } 

    Вероятно, у вас есть некоторые тела функций внутри файла .h в classе, который использует неполный тип.

    Убедитесь, что внутри windows .h для classа вы имеете только объявление функции. Все тела функций для windows должны находиться в файле .cpp. И для windows_impl …

    Btw, вы должны явно добавить объявление деструктора для classа Windows в ваш .h-файл.

    Но вы НЕ МОЖЕТЕ поместить пустой файл dtor в заголовочный файл:

     class window { virtual ~window() {}; } 

    Должно быть просто заявление:

      class window { virtual ~window(); } 

    использовать пользовательский дебит

    Проблема заключается в том, что unique_ptr должен вызывать деструктор T::~T() в свой собственный деструктор, его оператор назначения перемещения и unique_ptr::reset() функцию unique_ptr::reset() (only). Однако они должны быть вызваны (неявно или явно) в нескольких ситуациях PIMPL (уже в деструкторе внешнего classа и операторе присваивания перемещения).

    Как уже указывалось в другом ответе, одним из способов избежать этого является перемещение всех операций, требующих unique_ptr::~unique_ptr() , unique_ptr::operator=(unique_ptr&&) и unique_ptr::reset() в исходный файл, где class-помощник pimpl фактически определен.

    Однако это довольно неудобно и в какой-то степени бросает вызов самой точке идома. Это гораздо более чистое решение, которое позволяет избежать всего, что связано с использованием пользовательского делетера, и только переместить его определение в исходный файл, в котором живет помощник-помощник прыща. Вот простой пример:

     // file.h class foo { struct pimpl; struct pimpl_deleter { void operator()(pimpl*) const; }; std::unique_ptr _pimpl; public: foo(some data); foo(foo&&) = default; // no need to define this in file.cc foo&operator=(foo&&) = default; // no need to define this in file.cc //foo::~foo() auto-generated: no need to define this in file.cc }; // file.cc struct foo::pimpl { // lots of complicated code }; void foo::pimpl_deleter::operator()(foo::pimpl*ptr) const { delete ptr; } 

    Вместо отдельного classа deleter вы также можете использовать свободную функцию или static член foo в сочетании с лямбдой:

     class foo { struct pimpl; static void delete_pimpl(pimpl*); std::unique_ptr _pimpl; }; 

    Чтобы добавить к ответам другого пользователя о пользовательском удалении, в нашей внутренней «библиотеке утилит» я добавил вспомогательный заголовок для реализации этого общего шаблона ( std::unique_ptr неполного типа, известного только некоторым из ТУ, чтобы, например, избежать длинной компиляции или предоставить просто непрозрачную ручку для клиентов).

    Он предоставляет общие строительные леса для этого шаблона: пользовательский class делетера, который вызывает внешнюю функцию делетера, псевдоним типа unique_ptr с этим classом deleter и макрос, чтобы объявить функцию делетера в TU, которая имеет полное определение типа. Я думаю, что это имеет некоторую общую полезность, поэтому вот оно:

     #ifndef CZU_UNIQUE_OPAQUE_HPP #define CZU_UNIQUE_OPAQUE_HPP #include  /** Helper to define a `std::unique_ptr` that works just with a forward declaration The "regular" `std::unique_ptr` requires the full definition of `T` to be available, as it has to emit calls to `delete` in every TU that may use it. A workaround to this problem is to have a `std::unique_ptr` with a custom deleter, which is defined in a TU that knows the full definition of `T`. This header standardizes and generalizes this trick. The usage is quite simple: - everywhere you would have used `std::unique_ptr`, use `czu::unique_opaque`; it will work just fine with `T` being a forward declaration; - in a TU that knows the full definition of `T`, at top level invoke the macro `CZU_DEFINE_OPAQUE_DELETER`; it will define the custom deleter used by `czu::unique_opaque` */ namespace czu { template struct opaque_deleter { void operator()(T *it) { void opaque_deleter_hook(T *); opaque_deleter_hook(it); } }; template using unique_opaque = std::unique_ptr>; } /// Call at top level in a C++ file to enable type %T to be used in an %unique_opaque #define CZU_DEFINE_OPAQUE_DELETER(T) namespace czu { void opaque_deleter_hook(T *it) { delete it; } } #endif 
    Давайте будем гением компьютера.