The following https://godbolt.org/z/5qd8WbYz9 doesn't work and after reading the duplicate I thought I would have to define an custom deleter
However, I managed to get it working https://godbolt.org/z/dzYvsdKP1 by
BB.cpp
(to avoid implicit in-class default constructor){nullptr}
in-class initialization of the unique_ptr
Can someone explain why the {nullptr}
make's it break?
It's important to note that std::default_deleter<AA>
can be instantiated even when AA
is incomplete.
//BB.h
#include <memory>
struct AA;
struct BB
{
// B(); <- needed
~BB();
std::unique_ptr<AA> u{ nullptr }; // need to remove {nullptr} initializer
};
// BB.cpp
#include "BB.h"
struct AA {};
BB::~BB() = default;
// BB::BB() = default; <- needed
// main.cpp
#include "BB.h"
int main()
{
BB bb{};
}
AA
can not be an incomplete type at the time BB::~BB
is defined since AA
must be a complete type at the time std::default_delete::operator()
is called (which will happen in BB::~BB
. Otherwise, the program is ill-formed.
The reason for this requirement is that calling delete on an incomplete type is undefined behavior in C++ if the complete class type has a nontrivial destructor or a deallocation function, as the compiler has no way of knowing whether such functions exist and must be invoked.
The situation is the same as when you are implementing something using the pimpl idiom.
AA
can be incomplete when defining BB
.AA
must be complete when defining BB::~BB
.BB::BB
implicitly defined or = default
ed triggers a static_assert
by default_delete
in g++ and clang++ since it would then need to define unique_ptr<AA, default_delete<AA>>
s destructor too early:
~unique_ptr() { default_delete<AA>{}(the_pointer); } // oups, sizeof(AA) unknown
BB
member functions until AA
is defined is the idiomatic solution.