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
?
A class can be declared within the scope of another class. Such a class is called a "nested class." Nested classes are considered to be within the scope of the enclosing class and are available for use within that scope.
You cannot forward declare a nested structure outside the container. You can only forward declare it within the container.
In Objective-C, classes and protocols can be forward-declared if you only need to use them as part of an object pointer type, e.g. MyClass * or id<MyProtocol>.
Nested Classes in C++A nested class is a class that is declared in another class. The nested class is also a member variable of the enclosing class and has the same access rights as the other members. However, the member functions of the enclosing class have no special access to the members of a nested class.
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.
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.
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.
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