Question

In test.h:

#ifndef TEST_H
#define TEST_H

#include <map>
struct Incomplete;

class Test {
     std::map<int, Incomplete> member;
public:
    Test();
    int foo() { return 0; }
};

#endif

In test.cpp:

#include "test.h"
struct Incomplete {};
Test::Test() {}

In main.cpp

#include "test.h"

int main() {
    Test test;
    return test.foo();
}

g++ 4.7 gives me an error that struct Incomplete is forward declared when I write g++ main.cpp test.h -o main.o.

However, if I change std::map<int, Incomplete> member to std::map<int, Incomplete*> member, main.o compiles. Why is this?

Was it helpful?

Solution

Note: as of C++11, the explanation below is outdated: standard library containers now work with incomplete types.


Why is this?

Because the C++ standard library containers are not defined to work with incomplete member types. This is by design1 – but it’s arguably an error (and might be changed in future versions of C++). The Boost.Containers library fixes this.

Your code with pointers works because a pointer to an incomplete type is itself a complete type. However, this obviously changes the semantics of your type quite drastically (in particular, who manages the memory?) and it’s generally not a good idea to use this as a replacement.


1 It’s worth pointing out that the article claims that you technically cannot implement std::map to work with incomplete types. However, this claim is wrong.

OTHER TIPS

When you forward declare a type you basically declare that a type with that name exists, nothing more. The compiler does not know its size, members, etc. It is called an incomplete type and cannot be used for anything else than declaring pointers or references. This is possible because the size of a pointer, regardless its type is the same on the same platform.

A forward declaration means "this class/struct/whatever will be in the code later". That container needs to know how big the struct is to do the memory book-keeping. In technical terms, you're using an incomplete type.

Since a pointer is of a fixed size on most machines (x64 has usually 8 byte pointers), you don't need to see the full definition of your struct when compiling since you don't need to "calculate" how much space to reserve for that structure in your map: you just need the space needed for a pointer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top