Объявления — cppreference.com
Объявления это то, как вводятся (или повторно вводятся) имена в программу на C++. Не все объявления на самом деле объявляют что-либо, и каждый тип объекта объявляется по-разному. Определения это объявления, которых достаточно для использования сущности, идентифицируемой по имени.
Существуют следующие объявления:
- Определение функции
- Объявление шаблона (включая Частичную специализацию шаблона)
- Явное создание экземпляра шаблона
- Явная специализация шаблона
- Определение пространства имён
- Спецификация связывания
|
(начиная с C++11) |
- Пустое объявление (
;) - Объявление функции без последовательности-спецификаторов-объявления:
атрибуты (необязательно) декларатор ;
|
|||||||||
| атрибуты | — | (начиная с C++11) последовательность любого числа атрибутов |
| декларатор | — | объявление функции |
- Это объявление должно содержать конструктор, деструктор, или определённый пользователем тип функции преобразования. Это может быть использовано, как часть объявления шаблона, явной специализации шаблона, явного создания экземпляра шаблона.
- объявление-блока (объявление, которое может появиться внутри блока), который, в свою очередь, может быть одним из следующего:
- простое объявление
Простое объявление
Простое объявление, это выражение, которое вводит, создаёт, и, необязательно, инициализирует один или несколько идентификаторов, как правило, переменных.
последовательность-спецификаторов-объявления список-инициализации-деклараторов (необязательно) ;
|
(1) | ||||||||
атрибуты последовательность-спецификаторов-объявления список-инициализации-деклараторов;
|
(2) | ||||||||
| атрибуты | — | (начиная с C++11) последовательность любого числа атрибутов |
| последовательность-спецификаторов-объявления | — | последовательность спецификаторов (смотрите ниже). |
| список-инициализации-деклараторов | — | разделённый запятыми список объявлений с необязательными инициализаторами. список-инициализации-деклараторов необязателен, когда объявление, это именованные класс/структура/объединение или именованное перечисление |
Определение структурных привязок так же простое объявление. (начиная с C++17)
Спецификаторы
Объявленные спецификаторы, (последовательность-спецификаторов-объявления) это последовательность следующих, разделённых пробельными символами, спецификаторов в любой последовательности:
- спецификатор
typedef. Если присутствует, то это законченное объявление, состоящее из объявления typedef и объявлений, каждое из которых вводит новое имя типа, но не объект или функцию. - спецификаторы функций (
inline,virtual,explicit), разрешены только в объявлениях функций.
|
(начиная с C++17) |
- спецификатор
friend, разрешён в объявлении класса или функции.
|
(начиная с C++11) |
|
(начиная с C++20) |
- спецификатор класса памяти (register (до C++17), static, thread_local (начиная с C++11), extern, mutable). Разрешён только один спецификатор памяти, исключая
thread_local, который можно использовать вместе сexternилиstatic(начиная с C++11). - Спецификаторы типов (последовательность-спецификаторов-типов), спецификаторы типов, которые являются именами типов. Тип каждой сущности, введённой объявлением, это тип, необязательно модифицированный объявлением (смотрите ниже). Последовательность спецификаторов так же используется в type-id. Только следующие спецификаторы являются частью последовательности-спецификаторов-типа, в любом порядке:
- спецификатор class
- спецификатор enum
- спецификатор простого типа
- предварительно объявленное имя класса (необязательно полное)
- предварительно объявленное имя перечисления (необязательно полное)
- предварительно объявленные typedef-имена или псевдонимы типов (начиная с C++11) (необязательно полные)
- имя шаблона с аргументами шаблона (необязательно полное, необязательно использующее неоднозначности шаблона)
-
- ключевое слово class, struct или union, с последующим идентификатором (необязательно полным), ранее определённым, как имя класса, структуры или объединения.
- ключевое слово class, struct или union, с последующим именем шаблона с аргументами шаблона (необязательно полным, необязательно использующим неоднозначности шаблона), ранее определённым, как имя класса шаблона.
- ключевое слово enum с последующим идентификатором (необязательно полным), ранее определённым, как имя перечисления.
- только один тип спецификатора разрешён в последовательности-спецификаторов-объявления, со следующими исключениями:
- -
constможет сочетаться с любым типом спецификатора, исключая самого себя. - -
volatileможет сочетаться с любым типом спецификатора, исключая самого себя. - -
signedилиunsignedмогут сочетаться сchar,long,shortилиint. - -
shortилиlongмогут сочетаться сint. - -
longможет сочетаться сdouble.
|
(начиная с C++11) |
Атрибуты могут использоваться в последовательности-спецификаторов-объявления, в этом случае они применяются к типу, определённому предыдущими спецификаторами.
|
Спецификатор, который может появляться дважды в последовательности-спецификаторов-объявления это |
(начиная с C++17) |
Объявления
список-деклараторов-инициализации это разделённая запятыми последовательность одного или более деклараторов-инициализации, которая имеет следующий синтаксис:
| декларатор инициализатор (необязательно) | (1) | ||||||||
| декларатор предложение-requires | (2) | (начиная с C++20) | |||||||
| декларатор | — | объявление |
| инициализатор | — | необязательный инициализатор (за исключением случаев, когда он требуется, таких как инициализация ссылок или константных объектов). Для получения подробностей смотрите Инициализация. |
| предложение-requires | — | предложение requires, которое добавляет ограничения к объявлению функции |
Каждый декларатор-инициализации в последовательности инициализирующих объявлений S D1, D2, D3; обрабатывается, как если бы это было автономное объявление с теми же спецификаторами: S D1; S D2; S D3;.
Каждое объявление вводит исключительно один объект, ссылку, функцию, или (для объявления typedef) тип псевдонима, чей тип обеспечивается последовательностью-спецификаторов-объявления и, необязательно, модифицируется операторами & (ссылка на) или [] (массив) или () (возврат функции) в объявлении. Эти операторы могут применяться рекурсивно, как показано ниже.
декларатор это одно из следующего:
| неполный-id атрибуты (необязательно) | (1) | ||||||||
| полный-id атрибуты (необязательно) | (2) | ||||||||
... идентификатор атрибуты (необязательно)
|
(3) | (начиная с C++11) | |||||||
* атрибуты (необязательно) cv (необязательно) декларатор
|
(4) | ||||||||
спецификатор-вложенного-имени * атрибуты (необязательно) cv (необязательно) декларатор
|
(5) | ||||||||
& атрибуты (необязательно) декларатор
|
(6) | ||||||||
&& атрибуты (необязательно) декларатор
|
(7) | (начиная с C++11) | |||||||
декларатор-не-указатель [ constexpr (необязательно) ] атрибуты (необязательно)
|
(8) | ||||||||
декларатор-не-указатель ( список-параметров ) cv (необязательно) ссылка (необязательно) except (необязательно) атрибуты (необязательно)
|
(9) | ||||||||
1) Объявленное имя.
4) Объявление указателя: объявление S * D;, которое объявляет D как указатель на тип, определённый как последовательность-спецификаторов-объявления S.
6) Объявление lvalue ссылки: объявление S & D; объявляет D как lvalue ссылку на тип, определённый как последовательность-спецификаторов-объявления S.
7) Объявление rvalue ссылки: объявление S && D; объявляет D как rvalue ссылку на тип, определённый как последовательность-спецификаторов-объявления S.
8) Объявление массива. декларатор-не-указатель любое допустимое объявление, но если оно начинается с *, &, или &&, и окружено круглыми скобками.
9) Объявление функции. декларатор-не-указатель любое допустимое объявление, если оно начинается с *, & или &&, и окружено круглыми скобками. Обратите внимание, что объявление внешней функции может, необязательно, заканчиваться типом возвращаемого значения. (начиная с C++11)
|
Во всех случаях, атрибуты необязательная последовательность атрибутов. Когда появляются сразу после идентификатора, то применяются к объявляемому объекту. |
(начиная с C++11) |
cv последовательность квалификаторов const и volatile, где каждый квалификатор может появляться в последовательности больше одного раза.
Примечание
Когда объявление-блока появляется внутри блока, и идентификатор, введённый объявлением, которое уже было объявлено во внешнем блоке, скрывает внешнее объявление для оставшейся части блока.
Если объявление вводит переменную с автоматическим классом памяти, то эта переменная будет инициализирована, когда выполнится её выражение объявления. Все автоматические переменные, объявленные в блоке, уничтожаются при выходе из блока (независимо от того, как произошёл выход из блока: через исключение, goto, или по достижении конца блока), в порядке противоположном их инициализации.
Примеры
Примечание: этот пример демонстрирует, как некоторые сложные объявления анализируются с точки зрения грамматики языка. Другие популярные мнемоники: правило спирали, чтение наизнанку и использование зеркальных объявлений. Существует также автоматический анализатор на https://cdecl.org.
#include <type_traits> struct S { int member; // последовательность-деклараторов-объявления равна "int" // декларатор является "элементом" } obj, *pObj(&obj); // последовательность-деклараторов-объявления равна "struct S { int member; }" // декларатор "obj" объявляет объект типа S // декларатор "*pObj" объявляет указатель на S, // и инициализатор "(&obj)" инициализирует его int i = 1, *p = nullptr, f(), (*pf)(double); // последовательность-деклараторов-объявления равна "int" // декларатор "i" объявляет переменную типа int, // и инициализатор "= 1" инициализирует её // декларатор "*p" объявляет переменную типа int*, // и инициализатор "= nullptr" инициализирует её // декларатор "f()" объявляет (но не определяет) // функцию, не принимающую аргументов и возвращающую int // декларатор "(*pf)(double)" объявляет указатель на функцию, // принимающую double и возвращающую int int (*(*var1)(double))[3] = nullptr; // последовательность-деклараторов-объявления равна "int" // декларатор "(*(*var1)(double))[3]" // инициализотор "= nullptr" // 1. декларатор "(*(*var1)(double))[3]" является декларатором массива: // Объявленный тип: "(*(*var1)(double))" массив из 3 элементов // 2. декларатор "(*(*var1)(double))" является декларатором указателя: // Объявленный тип: "(*var1)(double)" указатель на массив из 3 элементов // 3. декларатор "(*var1)(double)" является декларатором функции: // Объявленный тип: функция "(*var1)", принимающая "(double)", // возвращает указатель на массив из 3 элементов. // 4. декларатор "(*var1)" является декларатором указателя: // Объявленный тип: "var1" указатель на функцию, принимающую "(double)", // возвращающую указатель на массив из 3 элементов. // 5. декларатор "var1" является идентификатором. // Это объявление объявляет объект var1 типа "указатель на функцию, // принимающую double и возвращающую указатель на массив из 3 элементов типа int" // Инициализатор "= nullptr" предоставляет начальное значение этого указателя. // Альтернативный синтаксис С++11: auto (*var2)(double) -> int (*)[3] = nullptr; // последовательность-деклараторов-объявления равна "auto" // декларатор "(*var2)(double) -> int (*)[3]" // инициализотор "= nullptr" // 1. декларатор "(*var2)(double) -> int (*)[3]" является декларатором функции: // Объявленный тип: функция "(*var2)", принимающая "(double)", возвращающая "int (*)[3]" // ... int main() { static_assert(std::is_same_v<decltype(var1), decltype(var2)>); }
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 482 | C++98 | деклараторы повторных объявлений не могли быть квалифицированы |
разрешены квалифицированные деклараторы |
| CWG 569 | C++98 | одна автономная точка с запятой не была допустимым объявлением |
пустое обьявление, которое не имеет эффекта |
| CWG 1830 | C++98 | разрешено повторение спецификатора функции в последовательности-спецификаторов-объявления |
повторение запрещено |