14

Consider the code below:

struct Foo {
    struct Bar;
    Foo()
    {
        Bar bar; // Why isn't Bar an incomplete type?!
    }
    struct Bar {}; // Full definition
};

// struct Bar {}; // fails to compile due to incomplete type

int main()
{
    Foo foo;
}

It compiles fine under at least 2 compilers (gcc5.2, clang3.5). My question is:

  • Why isn't Bar considered an incomplete type in the constructor Foo::Foo, as I forward-declare it above the constructor but fully use it inside the constructor?

Whenever I move Foo::Bar outside the class, in other words Bar becomes a stand-alone class, I get the expected

error: aggregate 'Foo::Bar bar' has incomplete type and cannot be defined

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 2
    Member function bodies behave as if they are defined out-of-line (i.e., after the class definition). – T.C. Sep 20 '15 at 23:25
  • The main reason is that compiler can do local search of all the definitions inside the class, when it determines how to handle each type. The scope is examined in full, since it's just a tiny part of your program, and it doesnt need to rely on order of declarations in the source code. – tp1 Sep 20 '15 at 23:28

1 Answers1

8

Within the member specification the class is considered complete within function bodies, from the draft C++ standard section 9.2 [class.mem]:

A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, using-declarations introducing inheriting constructors (12.9), exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification

Which means you don't even have to forward declare Bar (see it live):

struct Foo {
    Foo()
    {
        Bar bar; 
    }
    struct Bar {};  
};

Forward declaring could be useful in avoiding violation of section 3.3.7 paragraph 2 and 3.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Thanks! Regarding your latest edit, it seems I have to forward declare whenever the inner class has a different access specifier. See e.g. [this](http://stackoverflow.com/a/32685194/3093378), if I remove the forward declaration the code won't compile (tried with both gcc and clang). – vsoftco Sep 21 '15 at 12:47
  • @vsoftco right, in that specific case it does work without the forward declaration since that is not one of the places where the class is considered complete as defined by the paragraph I quote above. – Shafik Yaghmour Sep 21 '15 at 13:01