From Is std::unique_ptr required to know the full definition of T?, I know that if a class A
has a member unique_ptr<T>
, then T
shall be a complete type in destructor ~A()
. However, I came across a situation that the constructor A()
also requires complete type of T
, see code below:
// a.h -------------------------------
#pragma once
#include <memory>
struct B;
struct A {
A(); // <---
~A();
std::unique_ptr<B> ptr;
};
// a.cpp -------------------------------
#include "a.h"
struct B {};
A::A() = default; // <---
A::~A() = default;
// main.cpp -------------------------------
#include "a.h"
int main() {A a;}
If the definition of constructor A::A()
is moved to the header a.h
, the compiler will complain error: invalid application of ‘sizeof’ to incomplete type ‘B’
. Why is this happening? Is there any reference material about this?
BTW, I’m using gcc-7.5.0 on Ubuntu 18.04, with c++17 enabled.
Edit for @463035818_is_not_a_number in the comments. The complete error message is:
[1/2] Building CXX object CMakeFiles/t.dir/main.cpp.o
FAILED: CMakeFiles/t.dir/main.cpp.o
/usr/bin/c++ -g -fdiagnostics-color=always -std=gnu++1z -MD -MT CMakeFiles/t.dir/main.cpp.o -MF CMakeFiles/t.dir/main.cpp.o.d -o CMakeFiles/t.dir/main.cpp.o -c /home/user/Tests/UniquePtrTest/main.cpp
In file included from /usr/include/c++/7/memory:80:0,
from /home/user/Tests/UniquePtrTest/a.h:2,
from /home/user/Tests/UniquePtrTest/main.cpp:1:
/usr/include/c++/7/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]’:
/usr/include/c++/7/bits/unique_ptr.h:263:17: required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]’
/home/user/Tests/UniquePtrTest/a.h:5:3: required from here
/usr/include/c++/7/bits/unique_ptr.h:76:22: error: invalid application of ‘sizeof’ to incomplete type ‘B’
static_assert(sizeof(_Tp)>0,
^
ninja: build stopped: subcommand failed.
2
Answers
The issue is that
A::A()
needs to know how to destroyptr
in case the constructor throws.An example:
generates:
showing the call to
std::unique_ptr<B, std::default_delete<B> >::~unique_ptr()
.Technically, yes, you can read the Standard which defines which functions/expressions require a complete type.
Practically, not so much, since you have to read the Standard which defines which functions/expressions require a complete type.
Of course cppreference is of high quality and actually readable, although I did not find this use case there.
In particular, this issue is mentioned in a note
20.11.1.3.3 Destructor [unique.ptr.single.dtor]
The error you see is
std::default_deleter
guarding against undefined behaviour for you.When you instantiate the definition of the constructor
std::unique_ptr<B>::unique_ptr
, the definition ofstd::default_delete<B>::operator()
is also instantiated. Within which is an assertionwhich checks for incomplete types. This prevents any possible deletion of an incomplete type, which is undefined behaviour. See also incomplete types with shared_ptr and unique_ptr.
But why does moving the definition of
A::A()
to the header cause an error but not if it’s in the implementation file?As it turns out, simply declaring the member
std::unique_ptr<B>
only instantiates the declaration of its constructor but not the definition. Therefore ifA::A()
is defined in the implementation file, the definition ofstd::default_delete<B>::operator()
is also only instantiated then, by whichB
is a complete type.