The implementation of Foo_impl
must be complete prior to the instantiation required in std::unique_ptr<Foo_impl> m_impl = nullptr
.
Leaving the type declared (but not initialised) will fix the error (std::unique_ptr<Foo_impl> m_impl;
), you would then need to initialise it later on in the code.
The error you are seeing is from the implementation of a technique used to test for this; the incomplete type. Basically, sizeof
will result in an error with types that are only forward declared (i.e. lack definition when used at that point in the code/compilation).
A possible fix here would look like;
class Foo_impl;
class Foo
{
// redacted
public:
Foo();
~Foo();
private:
Foo(const Foo&);
Foo& operator=(const Foo&);
std::unique_ptr<Foo_impl> m_impl;// = nullptr;
};
class Foo_impl {
// ...
};
Foo::Foo() : m_impl(nullptr)
{
}
Why is the complete type required?
The instantiation via = nullptr
uses copy initialisation and requires the constructor and destructor to be declared (for unique_ptr<Foo_impl>
). The destructor requires the deleter function of the unique_ptr
which, by default, calls delete
on the pointer to Foo_impl
hence it requires the destructor of Foo_impl
, and the destructor of Foo_impl
is not declared in the incomplete type (the compiler doesn't know what it looks like). See Howard's answer on this as well.
Key here is that calling delete
on an incomplete type results in undefined behaviour (§ 5.3.5/5) and hence is explicitly checked for in the implementation of unique_ptr
.
Another alternative for this situation may be to use direct initialisation as follows;
std::unique_ptr<Foo_impl> m_impl { nullptr };
There seems to be some debate on the non-static data member initialiser (NSDMI) and whether this is a context that requires the member definition to exist, at least for clang (and possibly gcc), this seems to be such a context.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…