16
votes

Say I have two classes:

"Foo.h"

#pragma once    
class Foo
{
public:
    Foo()
    {

    };

    ~Foo()
    {

    };
};

"A.h"

#pragma once
#include <memory>

class Foo;

class A
{
public:
    A(){};
    ~A(){};

    std::unique_ptr<Foo> foo;
};

A holds a unique_ptr of Foo. I didn't want to include Foo in "A.h", so I forward declared it. By just forward declaring the class Foo in "A.h", I get a compile time error:

error C2027: use of undefined type 'Foo'
error C2338: can't delete an incomplete type  

So I was following this article on how to avoid this error and moved A's destructor in it's own .cpp file where I also include Foo:

"A.cpp"

#include "A.h"

#include "Foo.h"

A::A()
{

}

A::~A()
{

}

After implementing the destructor of A in "A.cpp", I'm able to compile the program, because the class Foo is known in "A.cpp". This seems logical, because unique_ptr needs the complete type to call it's destructor. But to my surprise, after commenting out the constructor of A (in "A.h" as well as "A.cpp"), I get the same error. How is this possible? Why does the compiler complain about beeing not able to call Foo's destructor when A has no constructor?

EDIT: I uploaded the 4 files so you can test the program. I'm using MSVC++ of Visual Studio 2013.

http://www.filedropper.com/test_61

2
A does have a constructor when you comment out your constructor: a default constructor is provided by the compiler, and this constructor gets an inline definition.dyp
When you comment out the constructor the compiler will define it implicitly, as an inline function (i.e. in the header not in the .cpp file). It appears as though your compiler thinks the implicitly-defined default constructor might want to destroy the unique_ptr, possibly in case the body of the constructor (or another member) throws an exception and the unique_ptr member needs to be destroyed again. The code you showed above should not have that effect, because there are no other members.Jonathan Wakely
In the code above, the default constructor should not need to destroy it (so if the compiler thinks it needs to that's a compiler bug) but if your real code has other members in the class defined after the unique_ptr member, and constructing one of those members can fail with an exception, the unique_ptr member would need to be destroyed. Are you showing the exact code you're testing, or a simplified version that doesn't actually give the same error?Jonathan Wakely
Oops, I'm wrong, the constructor does need to know the complete type, for exactly the reasons Chris Drew gives below. So the constructor needs to be defined non-inline along with the destructor.Jonathan Wakely
So a class ALWAYS needs the complete type for both it's constructor and destructor when using unique_ptr? I'm a litte confused because all articles you find on the web just mention the need of the full type in the destructor (like in the article I postet in my initial question).abcheudg234

2 Answers

23
votes

The constructor needs access to the deleter in just the same way the destructor does: exception safety requires that the constructor be able to roll-back initialisation of all the members in the case that your constructor's body throws:

[C++14: 12.6.2/10]: In a non-delegating constructor, the destructor for each potentially constructed subobject of class type is potentially invoked (12.4). [ Note: This provision ensures that destructors can be called for fully-constructed sub-objects in case an exception is thrown (15.2). —end note ]

Related:

-1
votes

It is no possible for 'A' to not have a constructor.

If you comment the constructor you wrote, the compiler will make a default constructor for you and it won't necessarily be in the same place you defined the one you made. Causing said problem.