3

g++ -std=c++11 does not compile a class that contains a container that contains unique pointers that point to a forward declared class. Questions:

  • Why?
  • Is there a reasonable workaround?

Code example:

#include <vector>
#include <memory>

// variant 1 (with full class definition): compiles
class Bar { [..] }; 
using BarPtr = std::unique_ptr<Bar>;

// variant 2 (with shared instead of unique pointers): compiles
using BarPtr = std::shared_ptr<class Bar>;

// variant 0 (which is what we want): compilation fails below
using BarPtr = std::unique_ptr<class Bar>;

// end of variants

class Foo {
    std::vector<BarPtr> vec;
 public:
    Foo() {} // compilation of variant 0 fails here:
             // In instantiation of ‘void std::default_delete<Bar>::operator()(Bar*) const
             // ... 
             // invalid application of ‘sizeof’ to incomplete type ‘Bar’
};

I have seen How to forward declare a class to be used in a standard container of unique_ptr and Is std::unique_ptr<T> required to know the full definition of T?, but do not find convincing answers to my above questions.

Community
  • 1
  • 1
Joachim W
  • 7,290
  • 5
  • 31
  • 59

4 Answers4

2

You need to move those parts of Foo to the implementation file, that need to full definition of Bar (see table by Howard Hinnant: https://stackoverflow.com/a/6089065/2173029). Following this guideline, this compiles:

#include <vector>
#include <memory>

class Bar;
using BarPtr = std::unique_ptr<Bar>;

class Foo {
    std::vector<BarPtr> vec;
 public:
    Foo(); // Needs Bar in implementation
    ~Foo();// Needs Bar in implementation
};
Community
  • 1
  • 1
Rumburak
  • 3,416
  • 16
  • 27
0

You may do this:

#include <vector>
#include <memory>

class Bar;
using BarPtr = std::unique_ptr<Bar>;

class Foo {
    std::vector<BarPtr> vec;
 public:
    Foo() {} // variant 3 fails here:
             // invalid application of ‘sizeof’ to incomplete type ‘Bar’
};


//Implementation goes here
class Bar{};
int main(){
    Foo a;
}

Live Demo

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
  • I confirm that this compiles under g++. The `main` program is not needed. However, the implementation `class Bar { [...] };` is. So the order in the source file does not matter. However, I want to implement class `Bar` in another compilation unit. – Joachim W Jan 06 '16 at 08:02
  • Yes main is just to make sure that everything is fine. When you want to a forward declaration you need to finally implement the class somewhere to not get linking problem or maybe in your case compile-error due to the absence of deleter (deconstructor) – Humam Helfawi Jan 06 '16 at 08:07
0

The problem is that you cannot delete by pointer to incomplete (forward declared) type. Make sure the definition of Bar is visible in the destructor of the containing class, or use a custom deleter for the unique_ptr that sees it. See also std::unique_ptr with an incomplete type won't compile

Community
  • 1
  • 1
Oberon
  • 3,219
  • 15
  • 30
0

The error message at line

Foo() {};

seems to tell that a destructor ~Bar() is required. But why? This part of my question is still open.

As for the practical solution, however, the answer is simple: Replace the above line by

Foo();

and implement Foo::Foo in a compilation unit that sees the full definition of class Bar.

Joachim W
  • 7,290
  • 5
  • 31
  • 59
  • how this differ from my answer? – Humam Helfawi Jan 06 '16 at 08:21
  • and be aware that if you did not implement a constructor, a default one will be implemented automatically. – Humam Helfawi Jan 06 '16 at 08:22
  • To the best of my knowledge, *declaration* of a constructor will prevent automatic *implementation* of a default. – Joachim W Jan 06 '16 at 08:52
  • This doesn't really explain the problem. The presence of a constructor implementation is not a problem. The problem is actually the absence of the `~Bar()` function (not anything to do with the Foo destructor either). Read the full error messages to see why - you disingenuously only pasted one line of the errors in the question. – M.M Jan 06 '16 at 10:49
  • I agree that the *Why* in my question is not yet answered. I edited the question and my partial answer. – Joachim W Jan 06 '16 at 11:59
  • 1
    @JoachimWuttke Regarding the *why*, read this: http://stackoverflow.com/questions/6012157/is-stdunique-ptrt-required-to-know-the-full-definition-of-t – vsoftco Jan 06 '16 at 14:47