Почему методы структур не должны быть объявлены в C++?

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

#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) был вызван до того, как метод был когда-либо объявленный.

3 ответов


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

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

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


стандарт C++ 3.4.1:

.4:

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

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

.5:

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

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

.7:

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

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

и, наконец, самое интересное:

3.3.7 область применения класса [основной.масштаб.класс]

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

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

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

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

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

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


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

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

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

для предсказуемого порядка у людей есть свои собственные стили, и есть немного "искусства", но, например, я использую каждый спецификатор доступа не более одного раза и всегда public до protected до private, тогда внутри тех, которые я обычно ставлю typedefs,const данные, конструкторы, деструкторы, мутируя/не-const функции, const функции statics,friends....

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

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

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

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

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