Reputation: 1873
The following simple piece of code compiles, although I don't understand why:
class C {
class B;
class A {
B getB() { return B(); }
};
class B {
};
};
int main(int, char**)
{
return 0;
}
If I then comment out the "class C
" stuff, so that the forward declaration of B
, the definition of A
and the definition of B
are no longer nested within a class, the code does not compile, since B
is of an incomplete type:
main.cpp: In member function 'B A::getB()':
main.cpp:6: error: return type 'struct B' is incomplete
main.cpp:6: error: invalid use of incomplete type 'struct B'
main.cpp:3: error: forward declaration of 'struct B'
I understand what it means for a type to be incomplete, namely that it has not been defined yet and so the compiler can't possibly know how much space to allocate for it. But why is B
not considered incomplete in the code above, where A
and B
are both declared and defined inside of C
?
Upvotes: 18
Views: 1666
Reputation: 741
Besides that when I've got the need to forward declare nested classes I tend to smell some bad design in my code, the trick I use is:
// Foo.h
class Foo {
class Bar {
};
};
class Foobar : public Foo::Bar {};
// Zoo.h
/* Fwd declare */
class FooBar;
Upvotes: 0
Reputation: 206557
The body of an inline member function is not processed until the class definition has been fully processed.
Hence, you can use:
class A
{
class B;
B getB() { return B(); }
class B {};
};
That also allows member variables that are not yet declared to be used in inline member function definition.
class Foo
{
int getBar() { return bar; }
int bar;
};
I am guessing the same logic is extended to inline definitions of member functions of nested classes -- i.e. they are not processed until the containing class definition is completely processed.
PS I am unable to quickly locate the reference in the standard that would verify my claim.
PS 2 The answer by Barry has the reference in the standard that makes the code in the question valid.
Upvotes: 9
Reputation: 302663
I believe this is a consequence of [basic.scope.class]:
The potential scope of a name declared in a class consists not only of the declarative region following the name’s point of declaration, but also of all function bodies, default arguments, exception-specifications, and brace-or-equal-initializers of non-static data members in that class (including such things in nested classes).
That is, the scope of the full declaration of B
includes the body of the member function of the nested class:
class C {
class B; // (1)
class A {
B getB() {
return B(); // both (1) and (2) in scope here
// since (2) is the complete type declaration,
// this is perfectly fine
}
};
class B { // (2)
};
};
By comparison, if C
were a namespace instead of a class, the scope of the full declaration of class B
would not extend into A::getB()
. The only visible declaration would be the forward-declaration of B
that I labeled (1)
- so B()
would be the construction of an incomplete type there.
Upvotes: 10
Reputation: 69854
The standard is explicit in mandating that the method's body is interpreted after the class that encloses it.
Thus at the time of evaluating the body of C::A::getB()
, A
, B
and C
are all complete types.
Upvotes: 5