0

I want two classes to 'include' each other in their variables.

#include <vector>

class Bar;

class Foo {
public:
    Bar b1;
};

class Bar {
public:
    std::vector<Foo> f1;
};

I get this error error: field 'b1' has incomplete type 'Bar' while trying to compile. What's going wrong?

avighnac
  • 376
  • 4
  • 12
  • Vector doesn't need declaration of templated class, since it has fixed size anyway, but plain `Bar b1` needs (see this as an example https://stackoverflow.com/a/15382714/3365922). Essentially you can forward declare `Foo`, then declare `Bar` and then declare `Foo` and everything will compile. – fas Oct 24 '22 at 04:29
  • Related/dupe: [How to create two classes in C++ which use each other as data?](https://stackoverflow.com/questions/4964482/how-to-create-two-classes-in-c-which-use-each-other-as-data). – Jason Oct 24 '22 at 04:40
  • While I am always careful with my designs to avoid circluar dependencies, it is sometimes needed. My solution to this is to basically replace the Bar member with a std::unique_ptr with custom deleter. This custom deleter allows us to postpone the definition of the destructor until after the full type of Bar is known. Live demo : https://godbolt.org/z/qvf5rcdKT – Pepijn Kramer Oct 24 '22 at 04:44

2 Answers2

1

Your C++ compiler doesn't know how large a Bar object is yet, so it can't be included in Foo. However, a pointer to Bar is a known size, so you can use that.

class Bar;

class Foo {
public:
    Bar *b1;
};
Chris
  • 26,361
  • 5
  • 21
  • 42
0

When declaring this:

class Bar;

class Foo {
public:
    Bar b1;
};

Think about it, Foo has no idea what Bar "looks" like, it just knows that Bar is a class, so what the sizeof Bar and therefore Foo should be?

On the other hand, when using a pointer, it doesn't need to know the size of the object, because the sizeof pointer is fixed and known, therefore you could do what @Chris proposed.

But since C++17, std::vector also accepts incomplete type so you could define this in the header:

bar.h

#include <vector>

class Foo;

class Bar
{
public:
    std::vector<Foo> f1;
};

foo.h

#include "bar.h"
class Foo
{
public:
    Bar b1;
};

Then include both bar.h and foo.h in both .cpp file.

thedemons
  • 1,139
  • 2
  • 9
  • 25
  • 2
    Something one should potentially be aware of: Only `std::vector`, `std::list` and `std::forward_list` are guaranteed to be usable like this with an incomplete type, and only since C++17. It may or may not work for other standard library containers. – user17732522 Oct 24 '22 at 04:35
  • @thedemons The 2nd code in your answer has undefined behavior before C++17. [Why does std::vector work with incomplete types in class definitions?](https://stackoverflow.com/questions/56975491/why-does-stdvector-work-with-incomplete-types-in-class-definitions). – Jason Oct 24 '22 at 04:41
  • @JasonLiam yes I should have clarified that. – thedemons Oct 24 '22 at 04:47