Объявления
Объявления это то, как вводятся (или повторно вводятся) имена в программу на C++. Не все объявления на самом деле объявляют что-либо, и каждый тип объекта объявляется по-разному. Определения это объявления, которых достаточно для использования сущности, идентифицируемой по имени.
Существуют следующие объявления:
- Определение функции
- Объявление шаблона (включая Частичную специализацию шаблона)
- Явное создание экземпляра шаблона
- Явная специализация шаблона
- Определение пространства имён
- Спецификация связывания
|
(начиная с C++11) |
- Пустое объявление (
;) - Объявление функции без последовательности-спецификаторов-объявления:
атрибуты (необязательно) декларатор ;
|
|||||||||
| атрибуты | — | (начиная с C++11) последовательность любого числа атрибутов |
| декларатор | — | объявление функции |
- Это объявление должно содержать конструктор, деструктор, или определённый пользователем тип функции преобразования. Это может быть использовано, как часть объявления шаблона, явной специализации шаблона, явного создания экземпляра шаблона.
- объявление-блока (объявление, которое может появиться внутри блока), который, в свою очередь, может быть одним из следующего:
| (начиная с C++11) |
| (начиная с C++20) |
|
(начиная с 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
- спецификатор простого типа
| (начиная с C++11) |
- предварительно объявленное имя класса (необязательно полное)
- предварительно объявленное имя перечисления (необязательно полное)
- предварительно объявленные typedef-имена или псевдонимы типов (начиная с C++11) (необязательно полные)
- имя шаблона с аргументами шаблона (необязательно полное, необязательно использующее неоднозначности шаблона)
|
(начиная с C++17) |
-
- ключевое слово 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) | ||||||||
S * D;, которое объявляет D как указатель на тип, определённый как последовательность-спецификаторов-объявления S.S C::* D;, объявляет D как указатель на элемент типа C, определённого как последовательность-спецификаторов-объявления S. спецификатор-вложенного-имени это последовательность имён и операторов разрешения области видимости ::S & D; объявляет D как lvalue ссылку на тип, определённый как последовательность-спецификаторов-объявления S.S && D; объявляет D как rvalue ссылку на тип, определённый как последовательность-спецификаторов-объявления S.|
Во всех случаях, атрибуты необязательная последовательность атрибутов. Когда появляются сразу после идентификатора, то применяются к объявляемому объекту. |
(начиная с C++11) |
cv последовательность квалификаторов const и volatile, где каждый квалификатор может появляться в последовательности больше одного раза.
| Этот раздел не завершён Причина: обьясняет правила сокрытия объявленного имени; как объявление переменной/функции скрывает класс (но не typedef) с таким же именем |
Примечание
Когда объявление-блока появляется внутри блока, и идентификатор, введённый объявлением, которое уже было объявлено во внешнем блоке, скрывает внешнее объявление для оставшейся части блока.
Если объявление вводит переменную с автоматическим классом памяти, то эта переменная будет инициализирована, когда выполнится её выражение объявления. Все автоматические переменные, объявленные в блоке, уничтожаются при выходе из блока (независимо от того, как произошёл выход из блока: через исключение, 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 | разрешено повторение спецификатора функции в последовательности-спецификаторов-объявления |
повторение запрещено |
Смотрите также
Документация C по Объявления
|