3

I thought that you could only create a reference or pointer member to a forward-declared class. However, I was surprised to discover this works:

#include <vector>

struct Donkey;

struct Cage
{
    std::vector<Donkey> donkeys;
};

struct Donkey
{
};

int main()
{
    Cage c;
}

http://ideone.com/EP0zKR

How come std::vector can be defined with a forward-declared class? Is this standard?

Neil Kirk
  • 21,327
  • 9
  • 53
  • 91
  • 1
    Because the vector basically contains a pointer to the type (and size/capacity and maybe some other stuff), but the important part here is the pointer. You don't need the complete definition of a type to declare a pointer of the type, that's why it works. – Some programmer dude Mar 25 '15 at 17:27
  • internally std::vector has a pointer to donkey, so you're still only defining a reference or pointer member to a forward declared class. – Robinson Mar 25 '15 at 17:27
  • 1
    Template type parameters don't generally have to be complete types. However, this particular template requires that your type be complete, and your program is ill-formed. – Kerrek SB Mar 25 '15 at 17:29
  • Possible duplicate? https://stackoverflow.com/questions/18672135/why-c-containers-dont-allow-incomplete-types – Praetorian Mar 25 '15 at 17:30
  • @Praetorian: How is "why does vector allow its element to be an incomplete type" a _duplicate_ of "why does vector **not** allow its element to be an incomplete type"? lol. It could not be more opposite. – Lightness Races in Orbit Mar 25 '15 at 17:35
  • 2
    @LightnessRacesinOrbit Well I guess it does answer my question! – Neil Kirk Mar 25 '15 at 17:36
  • @LightnessRacesinOrbit "Opposite" only because Neil made an incorrect assumption and worded his question accordingly. I didn't use the dupe hammer because of that difference in wording. Quite clearly even you agree that the answer to that question answers this one too :) – Praetorian Mar 25 '15 at 17:48
  • @Praetorian: Yeah but I don't always agree that similar answers = duplicate question :) – Lightness Races in Orbit Mar 25 '15 at 17:49

2 Answers2

5

Actually, you can't.

Just because your program compiles (which is down to facts of the underlying implementation) does not mean it is valid.

There are times other than declaring a T* or a T& at which you may use a forward declaration; it's just that this is not one of them.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Note that this is no longer true in C++17. [\[`std::vector`\] (but not its members) can be instantiated with an incomplete element type if the allocator satisfies the allocator completeness requirements](http://en.cppreference.com/w/cpp/container/vector) and [The default allocator satisfies allocator completeness requirements](http://en.cppreference.com/w/cpp/memory/allocator). – nwp Nov 14 '17 at 16:08
  • @nwp: Interesting. Without doing a deep dive on that change I'd be worried about relying on this before I'd cleared up in my head when the destructor (a member!) is and is not instantiated. – Lightness Races in Orbit Nov 15 '17 at 17:09
0

It's undefined behavior.

[C++14/§17.6.4.8] 1 In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.

[C++14/§17.6.4.8] 2 In particular, the effects are undefined in the following cases: [...]

  • if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.

Furthermore, an incomplete type doesn't meet the allocator requirements:

[C++14/§17.6.3.5] 9 An allocator may constrain the types on which it can be instantiated and the arguments for which its construct member may be called. If a type cannot be used with a particular allocator, the allocator class or the call to construct may fail to instantiate.

Because your class has a definition by the time the vector is instantiated, it's fine. Be on the lookout for this proposal which will change allocator requirements, N4056 Minimal incomplete type support for standard containers