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

Почему в С++ не должны быть объявлены методы structs?

Возьмем, например, следующий код:

#include <iostream>
#include <string>

int main()
{
    print("Hello!");
}

void print(std::string s) {
    std::cout << s << std::endl;
}

При попытке создать это, я получаю следующее:

program.cpp: In function ‘int main()’:
program.cpp:6:16: error: ‘print’ was not declared in this scope

Что имеет смысл.

Итак, почему я могу провести аналогичную концепцию в структуре, но не закричать на нее?

struct Snake {
    ...

    Snake() {
        ...
        addBlock(Block(...));
    }

    void addBlock(Block block) {
        ...
    }

    void update() {
        ...
    }

} snake1;

Не только я не получаю предупреждения, но программа действительно компилируется! Без ошибок! Это просто природа структур? Что здесь происходит? Очевидно, что addBlock(Block) был вызван до того, как был объявлен метод.

4b9b3361

Ответ 1

A struct в С++ на самом деле является определением class, где его содержимое public, если не указано иное, включая раздел protected: или private:

Когда компилятор увидит class или struct, он сначала переваривает все объявления внутри блока ({}) перед их работой.

В случае с обычным методом компилятор еще не видел объявленного типа.

Ответ 2

Стандарт С++ 3.4.1:

0,4 ​​

Имя, используемое в глобальной области, вне любой функции, класса или объявленное пользователем пространство имен, должно быть объявлено до его использования в глобальном сфера.

Вот почему глобальные переменные и функции не могут использоваться перед декларацией выше.

0,5

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

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

0,7

Имя, используемое в определении класса X вне члена тело функции или определение вложенного класса29 должно быть объявлено в одном из следующими способами: - до его использования в классе X или быть членом базовый класс X (10.2), или - если X является вложенным классом класса Y (9.7), до определения X в Y или должно быть членом базового класса (этот поиск применяется, в свою очередь, к закрывающим классам Y, начиная с самым внутренним охватывающим классом), 30 или - если X - локальный класс (9.8) или является вложенным классом локального класса, до определения класс X в блоке, охватывающем определение класса X, или - если X является член пространства имен N или является вложенным классом класса, который является член из N или является локальным классом или вложенным классом внутри локального класс функции, являющейся членом N, до определения класс X в пространстве имен N или в одном из Ns охватывающих пространств имен.

Я думаю, что это говорит обо всем коде, который не стоит в исполняемом коде процессора (например, декларативный код).

и, наконец, интересная часть:

3.3.7 Область видимости класса [basic.scope.class]

1 Следующие правила описывают область имен, объявленных в классах.

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

2) Имя N, используемое в классе S, относится к тому же декларации в ее контексте и при повторной оценке в завершенном сфера применения S. Диагностика не требуется для нарушения этого правила.

3) Если переупорядочение объявлений-членов в классе дает альтернативный действительный программа согласно (1) и (2), программа плохо сформирована, диагностика отсутствует требуется.

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

эффективно говоря, в классе декларация просматривается в двухфазной компиляции.

Ответ 3

" почему можно провести аналогичную концепцию в структуре, но не закричать на нее?"

В определении struct или class вы представляете публичный интерфейс для класса, и это намного проще понять, найти и поддерживать/обновлять этот API, если он представлен в:

  • предсказуемый порядок, с
  • минимальный беспорядок.

Для предсказуемого порядка у людей есть свои собственные стили и есть немного "искусства", но, к примеру, я использую каждый спецификатор доступа не более одного раза и всегда public до protected до private, тогда в пределах этих Я обычно помещаю typedef s, const данные, конструкторы, деструкторы, мутирующие/неконстантные функции, const функции, static s, friend s....

Чтобы свести к минимуму беспорядок, если функция определена в классе, она также может быть без предварительного объявления. У обоих есть тенденция только к запутыванию интерфейса.

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

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

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

  • Лучшая практика заключается в том, чтобы классы не вливались в чрезвычайно обширный интерфейс... вам обычно требуется функциональное ядро, а затем некоторые дискреционные функции удобства, после чего стоит рассмотреть, что можно добавить как функции, не являющиеся членами. std::string часто утверждает, что у него слишком много функций-членов, хотя я лично считаю это вполне разумным. Тем не менее, это также отличается от файла заголовка, объявляющего интерфейс библиотеки, где можно ожидать, что исчерпывающая функциональность будет переполнена, что сделает выделение даже inline более желательным.