Почему встроенное объявление не является неполным типом?

Рассмотрим приведенный ниже код:

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;
}

Он компилируется под минимум двумя компиляторами (gcc5.2, clang3.5). Мой вопрос:

  • Почему не Bar считается неполным типом в конструкторе Foo::Foo, поскольку я пересылаю его перед конструктором, но полностью использую его внутри конструктора?

Всякий раз, когда я перемещаю Foo::Bar вне класса, другими словами Bar становится автономным классом, я получаю ожидаемый

error: aggregate 'Foo:: Bar bar' имеет неполный тип и не может быть определен

Ответы

Ответ 1

В рамках спецификации члена класс считается завершенным в телах функций, из стандартного раздела проекта С++ 9.2 [class.mem]:

Класс считается полностью определенным типом объекта (3.9) (или полный тип) при закрытии} спецификатора класса. Внутри класса, класс считается полным в пределах функциональные тела, аргументы по умолчанию, использование-объявления, представляющие наследующие конструкторы (12.9), спецификации исключений и скопированные или равные инициализаторы для нестатических членов данных (в том числе такие вещи во вложенных классах). В противном случае он считается неполным в пределах своей собственной спецификации члена класса

Это означает, что вам даже не нужно пересылать объявление Bar (видеть его в прямом эфире):

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

Форвардное декларирование может быть полезно для предотвращения нарушения раздела 3.3.7 пункта 2 и 3.