Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pimpl with smart ptr - Why constructor/destructor needed [duplicate]

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?

like image 775
meddle0106 Avatar asked Feb 11 '14 10:02

meddle0106


1 Answers

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.

like image 137
Sebastian Redl Avatar answered Oct 29 '22 20:10

Sebastian Redl