具有不完整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_ptr
types不完整的例子。 问题在于破坏。
如果你用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_impl
types仍然不完整的地方被隐式定义。 除了他的解决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; };