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

Почему правило "Объявить перед использованием" не требуется внутри класса?

Мне интересно, почему правило объявления перед использованием С++ не содержится внутри класса.

Посмотрите на этот пример:

#ifdef BASE
struct Base {
#endif
    struct B;
    struct A {
        B *b;
        A(){ b->foo(); }
    };

    struct B {
        void foo() {}
    };
#ifdef BASE
};
#endif

int main( ) { return 0; }

Если BASE определена, код действителен.

Внутри конструктора я могу использовать B:: foo, который еще не был объявлен.

Почему это работает и, главным образом, почему работает внутри класса?

4b9b3361

Ответ 1

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

Ответ 2

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

На самом деле это одно из этих исключений. Определения функций членов на С++ специально и намеренно исключены из этого правила "объявить до использования" в том смысле, что поиск имен из тел этих членов выполняется так, как если бы они были определены после определения класса.

В спецификации языка указано, что в 3.4.1/8 (и сноске 30), хотя в нем используется другая формулировка. В нем говорится, что во время поиска имени из определения функции-члена проверяется определение всего класса, а не только часть выше определения функции-члена. В сноске 30 дополнительно указано, что правила поиска одинаковы для функций, определенных внутри определения класса или вне определения класса (что в значительной степени является тем, что я сказал выше).

Ваш пример немного нетривиальный. Возникает непосредственный вопрос об определениях функций-членов в вложенных классах: следует ли их интерпретировать так, как если бы они были определены после определения самого вмещающего класса? Ответ - да. 3.4.1/8 также охватывает эту ситуацию.

В "Design and Evolution of С++" описывается обоснование этих решений.

Ответ 3

Я не знаю главы и стиха этого стандарта.

Но если вы примените правило "объявить перед использованием" строго внутри класса, вы также не сможете объявлять переменные-члены в нижней части объявления класса. Сначала вы должны объявить их, чтобы использовать их, например. в списке инициализации конструктора.

Я мог представить, что правило "объявить перед использованием" было немного ослаблено в объявлении класса, чтобы обеспечить "более чистый" общий макет.

Просто догадки, как я уже сказал.

Ответ 4

Наиболее упрямые проблемы в определении С++ относятся к поиску имен: точно, какие имена используют имена, какие объявления? Здесь я опишу только одну проблему поиска: те, которые относятся к зависимостям заказов между объявлениями членов класса. [...]

Трудности возникают из-за конфликтов между целями:

  • Мы хотим иметь возможность выполнять синтаксический анализ, считывая только исходный текст.
  • Переупорядочение членов класса не должно изменять значение класса.
  • Тело функции-члена, явно написанное в строке, должно означать одно и то же, если оно записано вне строки.
  • Имена из внешней области должны использоваться из внутренней области (так же, как и на C).
  • Правила поиска имен должны быть независимыми от имени, на которое ссылается имя.

Если все это выполняется, язык будет достаточно быстро проанализировать, и пользователям не придется беспокоиться об этих правилах, потому что компилятор поймает двусмысленные и почти неоднозначные случаи. Существующие правила очень близки к этому идеалу.

[Дизайн и эволюция С++, раздел 6.3.1, называемые проблемами поиска на стр. 138]