Подтвердить что ты не робот

С++ Forward Объявление классов в классах

Следующий простой фрагмент кода компилируется, хотя я не понимаю, почему:

class C {
    class B;

    class A {
        B getB() { return B(); }
    };

    class B {
    };
};

int main(int, char**)
{
    return 0;
}

Если я затем прокомментирую материал "class C", так что объявление вперед B, определение A и определение B больше не вложены в класс, код делает не компилируется, так как B имеет неполный тип:

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'

Я понимаю, что это означает, что тип является неполным, а именно, что он еще не определен, и поэтому компилятор не может знать, сколько места выделяется для него. Но почему B не считается неполным в коде выше, где A и B объявлены и определены внутри C?

4b9b3361

Ответ 1

Я считаю, что это следствие [basic.scope.class]:

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

То есть область полного объявления B включает в себя тело функции-члена вложенного класса:

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

Для сравнения, если C было пространством имен вместо класса, область полного объявления класса B не распространялась бы на A::getB(). Единственным видимым объявлением будет объявление вперед B, которое я обозначил как (1), поэтому B() будет построением неполного типа.

Ответ 2

Тело встроенной функции-члена не обрабатывается до тех пор, пока определение класса не будет полностью обработано.

Следовательно, вы можете использовать:

class A 
{
   class B;
   B getB() { return B(); }

   class B {};
};

Это также позволяет переменные-члены, которые еще не объявлены для использования в определении функции встроенного члена.

class Foo
{
   int getBar() { return bar; }

   int bar;
};

Я предполагаю, что одна и та же логика распространяется на встроенные определения функций-членов вложенных классов, т.е. они не обрабатываются до тех пор, пока полное определение класса не будет полностью обработано.

PS Я не могу быстро найти ссылку в стандарте, подтверждающую мое требование.

PS 2 Ответ Барри содержит ссылку в стандарте, которая делает код в вопросе действительным.

Ответ 3

Стандарт явно указывается, что тело метода интерпретируется после класса, который его охватывает.

Таким образом, во время оценки тела C::A::getB(), A, B и C - все полные типы.

Ответ 4

Кроме того, когда мне нужно пересылать объявляемые вложенные классы, я, как правило, ощущаю плохой дизайн в своем коде, трюк, который я использую:

// Foo.h
class Foo {
    class Bar {
    };
};
class Foobar : public Foo::Bar {};


// Zoo.h
/* Fwd declare */
class FooBar;