Pimpl with smart ptr - Why constructor/destructor needed

c++, c++11, pimpl-idiom

Solution

The `unique_ptr` destructor needs to know the full definition of `AImpl`, because it deletes it. So the question is, where does the `unique_ptr` destructor sit? It's a template, so the question is about the instantiation point.

The destructor is instantiated when it is first used. Both the constructor and the destructor of the containing class use it (the constructor needs it if its body throws an exception). So the `unique_ptr` destructor is instantiated where `A`'s constructor or destructor are placed, whichever comes first.

If you default those special members, they are generated immediately after the class body, i.e. in the header, where the size of `AImpl` is not known.

If you instead declare them in the class and then put definitions (you may `=default` those definitions) in the `.cpp`, after the full definition of `AImpl`, then the `unique_ptr` destructor is instantiated there.

Problem

Lets consider following example (using c++11) A.hpp: ``` #include <memory> class A { public: //A(); //~A(); private: struct AImpl; std::unique_ptr<AImpl> pImpl; }; ``` main.cpp: ``` #include "A.hpp" int main() { A a; } ``` Using default constructor and destructor. Does not compile. Following error occurs: In file included from /usr/include/c++/4.8/memory:81:0, from A.hpp:2, from main.cpp:2: /usr/include/c++/4.8/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = A::AImpl]': /usr/include/c++/4.8/bits/unique_ptr.h:184:16: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = A::AImpl; _Dp = std::default_delete]' A.hpp:3:7: required from here /usr/include/c++/4.8/bits/unique_ptr.h:65:22: error: invalid application of 'sizeof' to incomplete type 'A::AImpl' static_assert(sizeof(_Tp)>0, A similar error occurs when using boost::scoped_ptr instead of std::unique_ptr. Do I understand this correctly - it means, that the forward declaration of AImpl is not enough? When adding constructor and destructor, everything works fine. What is the reason? Is it because the defaults are inlined an thus do not see the size of AImpl? And when adding constructor and destructor, the compiler assumes, that those definitions know the size of AImpl?

Original source

Related problems