Почему внутренние термины класса * не могут использовать родителя?
Известно, что не может быть членом типа, который вы определяете:
class Foo {
Foo member;
};
Причина в том, что это бесконечно рекурсивный, бесконечно большой объект. Однако мы можем иметь статические члены:
class Foo {
static Foo member;
};
Мы можем это сделать, потому что Foo
действует как пространство имен; экземпляры Foo
не содержат .member
, поэтому нет бесконечной ссылки. Другими словами, .member
принадлежит классу, а не экземпляру. То, что я хотел бы сделать, очень похоже:
class Foo {
class Bar {
Foo member;
};
};
И снова Foo
действует как пространство имен. Экземпляры Foo
фактически пусты. Мне нужно было бы создать нестатическое поле Bar Foo::bar;
, чтобы начать получать макеты. К сожалению, мои компиляторы не согласны (например GCC):
<source>:3:14: error: field 'member' has incomplete type 'Foo'
Foo member;
^~~~~~
По какой технической причине это не разрешено?
Ответы
Ответ 1
Короче говоря, легче было запретить это, чем позволять.
Вот пример, который показывает, что может быть сложно: С++ позволяет объединить вложенное определение класса с объявлением участника, например:
class Foo {
class Bar {
Foo member;
} bar; // <<== Here
};
Понятно, почему это определение должно быть запрещено: в отличие от определения класса, которое могло бы быть ОК, определение члена делает невозможным вычисление размера.
Конечно, авторы стандарта могли бы позволить определения классов проходить, за счет предоставления дополнительной работы компиляторам. Однако похоже, что они решили, что разрешить эту функцию не стоит проблем, поэтому они не сделали ее исключением из требования того, чтобы класс был полным в момент объявления экземпляра.
Ответ 2
Нет ничего плохого в том, что вам делать, и вы можете сделать это с помощью другого синтаксиса.
Поскольку компилятор хочет определить размер класса Bar, он должен знать размер класса Foo, но определение Foo еще не завершено (исходный код не был полностью проанализирован компилятором). Определение Foo должно быть завершено до его использования в Bar.
Вместо этого попробуйте переслать объявление в Foo, затем заполните определение Bar после Foo. Таким образом, размер Foo можно определить для использования в Bar.
class Foo {
class Bar;
};
class Foo::Bar {
Foo member;
};
Ответ 3
Это не разрешено, потому что вы не можете определить класс с членом с неполным типом, периодом. В конце определения класса класс становится полным, и это возможно только в том случае, если известны размеры всех его членов.
Например, вы получаете ту же ошибку по той же причине, не добавляя такие классы:
class Foo;
class Bar {
Foo member;
};
Конечно, в вашем примере язык может отложить завершение определения Foo::Bar
до тех пор, пока Foo
не будет определен, но это будет несовместимо с тем, как определяются классы в целом. У вас было бы странное поведение Foo::Bar
неполным в точке исходного кода после того, как оно было полностью определено.