Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't use std::unique_ptr<T> with T being a forward declaration

Now first, I am aware of the general issues with unique_ptr<> and forward declarations as in Forward declaration with unique_ptr? .

Consider these three files:

A.h

#include <memory>
#include <vector>

class B;

class A
{
public:
    ~A();

private:
    std::unique_ptr<B> m_tilesets;
};

C.cpp

#include "A.h"

class B {

};

A::~A() {

}

main.cpp

#include <memory>

#include "A.h"

int main() {
    std::unique_ptr<A> m_result(new A());
}

Issuing g++ -std=c++11 main.cpp C.cpp yields the following error:

In file included from /usr/include/c++/4.8/memory:81:0,
                 from main.cpp:1:
/usr/include/c++/4.8/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]’:
/usr/include/c++/4.8/bits/unique_ptr.h:184:16:   required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]’
A.h:6:7:   required from here
/usr/include/c++/4.8/bits/unique_ptr.h:65:22: error: invalid application of ‘sizeof’ to incomplete type ‘B’
  static_assert(sizeof(_Tp)>0,

That's true, B is an incomplete type in line 6 of A.h - but that's not where A's destructor is! g++ seems to generate a destructor for A even though I am providing one. A's destructor is in C.cpp line 7 and there B is a perfectly defined type. Why am I getting this error?

like image 350
hllnll Avatar asked Feb 07 '15 19:02

hllnll


People also ask

What is C++ forward declaration?

Forward Declaration refers to the beforehand declaration of the syntax or signature of an identifier, variable, function, class, etc. prior to its usage (done later in the program). Example: // Forward Declaration of the sum() void sum(int, int); // Usage of the sum void sum(int a, int b) { // Body }

Can Unique_ptr be copied?

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.

What is std :: Unique_ptr?

std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.

Why should I use forward declarations?

A forward declaration allows us to tell the compiler about the existence of an identifier before actually defining the identifier. In the case of functions, this allows us to tell the compiler about the existence of a function before we define the function's body.


2 Answers

You also need to put A's constructor in C.cpp:

A.h

#include <memory>
#include <vector>

class B;

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

private:
    std::unique_ptr<B> m_tilesets;
};

C.cpp

#include "A.h"

class B {

};

A::~A() {

}

A::A() {

}

See this answer. The constructor needs access to the complete type as well. This is so that it can call the deleter if an exception is thrown during construction.

like image 200
Chris Drew Avatar answered Oct 27 '22 11:10

Chris Drew


The implicitly defined special member functions are inline, leading to issues with incomplete types. As the link from Chris's answer shows, all non-delegating constructors could potentially invoke the destructor. This includes copy(deleted in this case) and move constructors as well. So, when you are dealing with non-static members involving incomplete types, explicitly default the definitions in the source file, thus ensuring that they aren't defined inline.

In header:

A();
~A();
A(const A&);
A(A&&);

In source:

A::A() = default;
A::~A() = default;
A::A(const A&) = default;
A::A(A&&) = default;
like image 35
Pradhan Avatar answered Oct 27 '22 11:10

Pradhan