С++ 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
?
Ответы
Ответ 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;