具有不完整types的std :: unique_ptr将不能编译

我用std::unique_ptr使用pimpl-idiom:

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

但是,在<memory>第304行中,出现了有关使用不完整types的编译错误:

sizeof ”应用于不完整的types“ uixx::window::window_impl

据我所知, std::unique_ptr应该能够使用一个不完整的types。 这是一个在libc ++中的错误,或者我在这里做错了什么?

这里有一些std::unique_ptrtypes不完整的例子。 问题在于破坏。

如果你用unique_ptr使用unique_ptr ,你需要声明一个析构函数:

 class foo { class impl; std::unique_ptr<impl> impl_; public: foo(); // You may need a def. constructor to be defined elsewhere ~foo(); // Implement (with an empty body) where impl is complete }; 

因为否则编译器会生成一个默认的,并且需要一个完整的foo::impl声明。

如果你有模板构造函数,那么即使你不构造impl_成员,也是如此:

 template <typename T> 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> impl_; 

因为编译器在这里必须知道如何销毁这个静态持续时间的对象。 解决方法是:

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

正如Alexandre C.所提到的那样,问题归结为window的析构函数在window_impltypes仍然不完整的地方被隐式定义。 除了他的解决scheme之外,我使用的另一个解决方法是在头文件中声明一个Deleter函子:

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

在类中使用不完整types的.h文件中可能有一些函数体。

确保在你的.h类窗口中只有函数声明。 窗口的所有函数体都必须在.cpp文件中。 而且对于window_impl以及…

顺便说一句,你必须显式添加析构函数声明的Windows类在你的.h文件。

但是你不能把空的DTOR正文放在你的头文件中:

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

必须只是一个声明:

  class window { virtual ~window(); } 

使用自定义删除器

问题是unique_ptr<T>必须在它自己的析构函数,移动赋值运算符和unique_ptr::reset()成员函数(仅)中调用析构函数T::~T() unique_ptr::reset() ()。 但是,这些必须在几个PIMPL情况下(隐式或显式地)调用(已经在外部类的析构函数和移动赋值操作符中)。

正如在另一个答案中已经指出的,避免这种情况的一种方法是将所有需要unique_ptr::~unique_ptr()unique_ptr::operator=(unique_ptr&&)unique_ptr::reset()到源文件中pimpl辅助类实际上是定义的。

然而这样做很不方便,在一定程度上蔑视了这个问题。 一个更干净的解决scheme,避免了所有的使用自定义删除器 ,只将其定义移动到pimple helper类所在的源文件中。 这是一个简单的例子:

 // file.h class foo { struct pimpl; struct pimpl_deleter { void operator()(pimpl*) const; }; std::unique_ptr<pimpl,pimpl_deleter> _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; } 

除了单独的删除器类以外,还可以使用foo的免费函数或static成员与lambda结合使用:

 class foo { struct pimpl; static void delete_pimpl(pimpl*); std::unique_ptr<pimpl,[](pimpl*p){delete_pimpl(p);}> _pimpl; };