Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Forward Declaring Classes Within Classes

Tags:

c++

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?

like image 675
villapx Avatar asked Mar 10 '16 20:03

villapx


People also ask

Can you declare a class within another class?

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.

Can I forward declare a nested class?

You cannot forward declare a nested structure outside the container. You can only forward declare it within the container.

Can you forward declare in C?

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>.

Can you declare a class within a class C++?

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.


3 Answers

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.

like image 144
Barry Avatar answered Sep 28 '22 04:09

Barry


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.

like image 37
R Sahu Avatar answered Sep 28 '22 05:09

R Sahu


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.

like image 41
Richard Hodges Avatar answered Sep 28 '22 06:09

Richard Hodges