Объявления это то, как вводятся (или повторно вводятся) имена в программу на C++. Не все объявления на самом деле объявляют что-либо, и каждый тип объекта объявляется по-разному. Определения это объявления, которых достаточно для использования сущности, идентифицируемой по имени.
Существуют следующие объявления:
- Пустое объявление (
;
)
- Объявление функции без последовательности-спецификаторов-объявления:
|
атрибуты (необязательно) декларатор ;
|
|
|
|
атрибуты
|
—
|
(начиная с C++11) последовательность любого числа атрибутов
|
декларатор
|
—
|
объявление функции
|
- Это объявление должно содержать конструктор, деструктор, или определённый пользователем тип функции преобразования. Это может быть использовано, как часть объявления шаблона, явной специализации шаблона, явного создания экземпляра шаблона.
- объявление-блока (объявление, которое может появиться внутри блока), который, в свою очередь, может быть одним из следующего:
-
-
-
[править] Простое объявление
Простое объявление, это выражение, которое вводит, создаёт, и, необязательно, инициализирует один или несколько идентификаторов, как правило, переменных.
|
последовательность-спецификаторов-объявления список-инициализации-деклараторов (необязательно) ;
|
(1)
|
|
|
атрибуты последовательность-спецификаторов-объявления список-инициализации-деклараторов;
|
(2)
|
|
|
атрибуты
|
—
|
(начиная с C++11) последовательность любого числа атрибутов
|
последовательность-спецификаторов-объявления
|
—
|
последовательность спецификаторов (смотрите ниже).
|
список-инициализации-деклараторов
|
—
|
разделённый запятыми список объявлений с необязательными инициализаторами. список-инициализации-деклараторов необязателен, когда объявление, это именованные класс/структура/объединение или именованное перечисление
|
Определение структурных привязок так же простое объявление. (начиная с C++17)
[править] Спецификаторы
Объявленные спецификаторы, (последовательность-спецификаторов-объявления) это последовательность следующих, разделённых пробельными символами, спецификаторов в любой последовательности:
- спецификатор
inline так же разрешён в объявлении переменных.
|
(начиная с C++17) |
- спецификатор
friend
, разрешён в объявлении класса или функции.
- спецификатор
constexpr , разрешён только в объявлении переменных, объявлении функций и шаблонов функций, а так же в объявлении статических членов данных и литеральных типов.
|
(начиная с C++11) |
- спецификатор
consteval , разрешён только в объявлениях функций и шаблонов функций.
- спецификатор
constinit , разрешён только в объявлении переменной со статическим или потоковым классом памяти. Разрешён только один спецификатор constexpr , consteval и constinit в последовательности-спецификаторов-объявления.
|
(начиная с C++20) |
- спецификатор класса памяти (register (до C++17), static, thread_local (начиная с C++11), extern, mutable). Разрешён только один спецификатор памяти, исключая
thread_local
, который можно использовать вместе с extern
или static
(начиная с C++11).
- Спецификаторы типов (последовательность-спецификаторов-типов), спецификаторы типов, которые являются именами типов. Тип каждой сущности, введённой объявлением, это тип, необязательно модифицированный объявлением (смотрите ниже). Последовател��ность спецификаторов так же используется в type-id. Только следующие спецификаторы являются частью последовательности-спецификаторов-типа, в любом порядке:
-
- char, char8_t (начиная с C++20), char16_t, char32_t (начиная с C++11), wchar_t, bool, short, int, long, signed, unsigned, float, double, void
-
-
- ключевое слово class, struct или union, с последующим идентификатором (необязательно полным), ранее определённым, как имя класса, структуры или объединения.
- ключевое слово class, struct или union, с последующим именем шаблона с аргументами шаблона (необязательно полным, необязательно использующим неоднозначности шаблона), ранее определённым, как имя класса шаблона.
- ключевое слово enum с последующим идентификатором (необязательно полным), ранее определённым, как имя перечисления.
- только один тип спецификатора разрешён в последовательности-спецификаторов-объявления, со следующими исключениями:
- -
const
может сочетаться с любым типом спецификатора, исключая самого себя.
- -
volatile
может сочетаться с любым типом спецификатора, исключая самого себя.
- -
signed
или unsigned
могут сочетаться с char
, long
, short
или int
.
- -
short
или long
могут сочетаться с int
.
- -
long
может сочетаться с double
.
- -
long может сочетаться с long .
|
(начиная с C++11) |
Атрибуты могут использоваться в последовательности-спецификаторов-объявления, в этом случае они применяются к типу, определённому предыдущими спецификаторами.
Спецификатор, который может появляться дважды в последовательности-спецификаторов-объявления это long (который может появляться дважды). Все другие повторения, такие как const static const , или virtual inline virtual являются ошибками.
|
(начиная с 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)
|
|
|
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
|
разрешено повторение спецификатора функции в последовательности-спецификаторов-объявления
|
повторение запрещено
|
[править] Смотрите также