Reputation: 4407
I'm trying to use unique_ptr for a pimpl idiom. So I'm declaring a destructor inside the class so the unique_ptr deletion is not instantiated where the impl class is not defined, and then I define it in another file.
This is my layout:
wrapper.h:
#pragma once
#include <memory>
struct Wrapper
{
class Impl;
~Wrapper();
std::unique_ptr<Impl> _impl;
};
wrapper.cpp:
#include "wrapper.h"
class Wrapper::Impl {};
Wrapper::~Wrapper() = default;
This file compiles just fine. However when compiling main.cpp I get incomplete type errors (see errors below):
main.cpp:
#include "wrapper.h"
int main()
{
Wrapper w;
return 0;
}
However, if I add the two lines from wrapper.cpp at the end of main.cpp, it compiles just fine. I don't understand two things:
What am I missing?
UPDATE:
Following @AdrianMole's suggestion I added a ctor declaration inside class Wrapper definition and for some reason it fixed the error, even though the error (and the unique_ptr spec) refers to the destructor.
Updated wrapper.h:
struct Wrapper
{
class Impl;
Wrapper();
~Wrapper();
std::unique_ptr<Impl> _impl;
};
So I'm adding a question:
These are the errors I get with MSVC, but similar errors happen with clang or gcc (I tried online compilers):
memory(2536,1): error C2027: use of undefined type 'Wrapper::Impl'
wrapper.h(7): message : see declaration of 'Wrapper::Impl'
memory(2535): message : while compiling class template member function 'void std::default_deleteWrapper::Impl::operator ()(_Ty *) noexcept const'
with [ _Ty=Wrapper::Impl ]
memory(2647): message : see reference to function template instantiation 'void std::default_deleteWrapper::Impl::operator ()(_Ty *) noexcept const' being compiled
with [ _Ty=Wrapper::Impl ]
memory(2574): message : see reference to class template instantiation 'std::default_deleteWrapper::Impl' being compiled
wrapper.h(9): message : see reference to class template instantiation 'std::unique_ptrWrapper::Impl,std::default_delete<Wrapper::Impl>' being compiled
memory(2536,25): error C2338: can't delete an incomplete type
memory(2537,1): warning C4150: deletion of pointer to incomplete type 'Wrapper::Impl'; no destructor called
wrapper.h(7): message : see declaration of 'Wrapper::Impl'
Upvotes: 2
Views: 593
Reputation: 40791
Any constructor definition (including the implicitly defined default constructor) potentially invokes the destructor of the types of all member objects of class type. The rationale for this is that if constructing a later member throws, the destructor of all previous members would need to be called. In the case of the last member, this is not strictly required, but the standard does not make this exception.
For example, if your class had been:
struct complete_type_with_throwing_constructor {
complete_type_with_throwing_constructor() { throw 0; }
};
struct Wrapper
{
class Impl;
~Wrapper();
std::unique_ptr<Impl> _impl;
complete_type_with_throwing_constructor x;
};
Then the implicit default constructor of Wrapper
would construct _impl
then destroy it after the default constructor of x
throws.
This is the reason why your code works if you explicitly declare your default constructor. This moves the definition to a point where the destructor of std::unique_ptr<Impl>
can be instantiated.
As for your second question, you are right that the code should still not work when adding the definition afterwards. It's just that compilers do not detect it, so they don't have an error.
Upvotes: 1