Search code examples
c++forward-declaration

std::unique_ptr<incomplete_type> without custom deleter


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

  • adding a default constructor implemented in BB.cpp (to avoid implicit in-class default constructor)
  • removing the {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{};
}

Solution

  • 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.

    cppreference:

    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.
    • Similarly, leaving BB::BB implicitly defined or = defaulted 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
      
    • Dererring the definitions of BB member functions until AA is defined is the idiomatic solution.