I was curious if was possible to create two classes, each of which holds a std::vector
of the other. My first guess was that it would not be possible, because std::vector
requires a complete type, not just a forward declaration.
#include <vector>
class B;
class A { std::vector<B> b; };
class B { std::vector<A> a; };
I would think that the declaration of std::vector<B>
would cause an immediate failure, because B
has an incomplete type at this point. However, this compiles successfully under both gcc and clang, without any warnings. Why doesn't this cause an error?
T.C commented that this is actually undefined behavior which is being addressed in a change request to the standard. The rule which is violated is apparently [res.on.functions] 2.5:
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.
The reason this works anyway (and the reason that it can be standardized) is two-fold:
Your program only contains type definitions; no objects are created, no functions are called, no code is generated. If we simplify it by eliminating B's definition (it's not needed) and then try to create an instance of A
, it fails:
class B;
class A { std::vector<B> b; };
A a; // error: ctor and dtor undefined for incomplete type B.
What also fails, as expected, is a simple
std::vector<B> b;
The reason in both cases is that the compiler must produce code, as opposed to a mere type declaration which is only grammatically relevant.
The use of the vector type is ok also in other contexts:
typedef std::vector<B> BVec;
Class A
can be defined because, as Nikolay correctly says in his answer, the size of std::vector<B>
, and hence the size of A
's member b
, does not depend on the definition of B
(because a vector holds a pointer to an array of elements, not an array proper).
This compiles and runs ok because std::vector
uses pointers and not the exact definition of A
or B
. You could modify your example to use single instance or array in the definition of the classes like so
class B;
class A { public: B b[2]; };
class B { public: A a[2]; };
This obviously fails to compile since you're trying to use definition of the classes. And the error by the compiler would be as you're expecting
error: field ‘b’ has incomplete type ‘B [2]’
However
class B;
class A { public: B* b; };
class B { public: A* a; };
would work just like std::vector
. So you don't need fully defined class to use pointer or reference to this type.
Also there is simplified example with templates
template<typename T>
struct C {
T* t;
};
class B;
class A { public: C<B> b; };
class B { public: C<A> a; };
This would also work and of course you can instantiate it
int main() {
B b;
A a;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With