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

1

10

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?

Socialize answered 11/2, 2014 at 10:27 Comment(1)
There is a good discussion of this issue in this GOTW.Sciuroid
A
14

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.

Appellee answered 11/2, 2014 at 10:32 Comment(2)
The destructor doesn't need to know the size; the memory management takes care of that. It does need to know how to destruct the object, however, and that supposes a complete class (to know whether there is a non-trivial destructor or not).Deanadeanda
@JamesKanze Good point, fixed it.Appellee

© 2022 - 2024 — McMap. All rights reserved.