Объявления — это способ ввода (или повторного ввода) имён в программу C++. Не все объявления фактически объявляют что-либо, и каждый вид сущности объявляется по-разному. Определения — это объявления, достаточные для использования сущности, идентифицируемой именем.
(с C++11) последовательность любого количества атрибутов
declarator
-
декларатор функции
Эта декларация должна объявлять конструктор, деструктор или функцию преобразования пользовательского типа преобразования. Она может использоваться только в составе объявления шаблона, явной специализации или явной инстанциации.
block-declaration (объявление, которое может появиться внутри блока), которое, в свою очередь, может быть одним из следующих:
(с C++11) последовательность любого количества атрибутов
decl-specifier-seq
-
последовательность спецификаторов (см. ниже)
init-declarator-list
-
список деклараторов, разделённых запятыми, с необязательными инициализаторами. init-declarator-list необязательно при объявлении именованного класса/структуры/объединения или именованного перечисления
Объявление связанных структур также является простым объявлением.(с C++17)
Спецификаторы
Спецификаторы объявления (decl-specifier-seq) — это последовательность следующих разделенных пробелами спецификаторов в любом порядке:
спецификатор typedef. Если он присутствует, всё объявление является объявлением typedef, и каждый декларатор вводит новое имя типа, а не объект или функцию.
спецификатор inline также разрешен в объявлениях переменных.
(с C++17)
спецификатор friend, разрешён в объявлениях классов и функций.
спецификатор constexpr, разрешен только в определениях переменных, объявлениях функций и шаблонных функций, и в объявлении статических данных-членов литерального типа.
(с C++11)
спецификатор consteval, разрешен только в объявлениях функций и шаблонных функций.
спецификатор constinit, разрешен только в объявлениях переменных со статической или потоковой областью хранения. Только один из спецификаторов constexpr, consteval, и constinit может присутствовать в decl-specifier-seq.
Спецификаторы типов (type-specifier-seq), последовательность спецификаторов, которые называют тип. Тип каждого объекта, введённого в объявление, является этим типом, необязательно изменённым декларатором (см. ниже). Эта последовательность спецификаторов также используется в type-id. Только следующие спецификаторы являются частью type-specifier-seq в любом порядке:
ключевое слово class, struct или union, за которым следует идентификатор (необязательно квалифицированный), ранее определённый как имя класса, структуры или объединения.
ключевое слово class, struct или union, за которым следует имя шаблона с аргументами шаблона (необязательно квалифицированное, необязательно с использованием дизъюнктора шаблона), ранее определённое как имя шаблона класса.
ключевое слово enum, за которым следует идентификатор (необязательно квалифицированный), ранее объявленный как имя перечисления.
разрешено только одно указание типа в decl-specifier-seq, за исключением следующих случаев:
const можно комбинировать с любым спецификатором типа, кроме самого себя.
volatile можно комбинировать с любым спецификатором типа, кроме самого себя.
signed или unsigned можно комбинировать с char, long, short или int.
short или long можно комбинировать с int.
long можно комбинировать с double.
long можно комбинировать с long.
(с C++11)
Атрибуты могут появляться в decl-specifier-seq, в этом случае они применяются к типу, определённому предыдущими спецификаторами.
Повторение любого спецификатора в decl-specifier-seq, например, const static const, или virtual inline virtual является ошибкой, за исключением того, что long может появляться дважды(с C++11).
Деклараторы
init-declarator-list — это последовательность из одного или более init-declarators, разделённых запятыми, имеющая следующий синтаксис:
declaratorinitializer (необязательно)
(1)
declaratorrequires-clause
(2)
(с C++20)
declarator
-
декларатор
initializer
-
необязательная инициализация (кроме случаев, когда она требуется, например, при инициализации ссылок или константных объектов). Подробности см. в Инициализации.
Каждый init-declarator в последовательности init-declarator обрабатывается так, как если бы это было самостоятельное объявление с теми же спецификаторами: S D1; S D2; S D3;.
Каждый декларатор вводит ровно один объект, ссылку, функцию или (для объявлений typedef) псевдоним типа, тип которого задаётся decl-specifier-seq и необязательно изменяется операторами, такими как & (ссылка на) или [] (массив из) или () (возвращающая функция) в деклараторе. Эти операторы могут применяться рекурсивно, как показано ниже.
6)Декларатор ссылки на lvalue: объявление S & D; объявляет D как ссылку на lvalue на тип, определяемый decl-specifier-seqS.
7)Декларатор ссылки на rvalue: объявление S && D; объявляет D как ссылку на rvalue на тип, определяемый decl-specifier-seqS.
8)Декларатор массива. noptr-declarator любой допустимый декларатор, но если он начинается с *, &, или &&, он должен быть заключён в скобки.
9)Декларатор функции. noptr-declarator любой допустимый декларатор, но если он начинается с *, &, или &&, он должен быть заключён в скобки. Он может заканчиваться необязательным возвращаемым типом.(с C++11)
Во всех случаях, attr — это необязательная последовательность атрибутов. При появлении непосредственно после идентификатора, она применяется к объявляемому объекту.
(с C++11)
cv — последовательность квалификаторов const и volatile, где любой квалификатор может появиться не более одного раза в последовательности.
Примечания
Когда block-declaration появляется внутри блока, и идентификатор, введённый объявлением, был ранее объявлен во внешнем блоке, внешнее объявление скрывается до конца блока.
Если объявление вводит переменную с автоматическим сроком жизни, она инициализируется при выполнении её оператора объявления. Все автоматические переменные, объявленные в блоке, уничтожаются при выходе из блока (независимо от того, как блок покинут: через исключение, goto или достижением его конца), в порядке, обратном их порядку инициализации.
#include <type_traits>
struct S
{
int member;
// decl-specifier-seq is "int"
// declarator is "member"
} obj, *pObj(&obj);
// decl-specifier-seq is "struct S { int member; }"
// declarator "obj" declares an object of type S
// declarator "*pObj" declares a pointer to S,
// and initializer "(&obj)" initializes it
int i = 1, *p = nullptr, f(), (*pf)(double);
// decl-specifier-seq is "int"
// declarator "i" declares a variable of type int,
// and initializer "= 1" initializes it
// declarator "*p" declares a variable of type int*,
// and initializer "= nullptr" initializes it
// declarator "f()" declares (but doesn't define)
// a function taking no arguments and returning int
// declarator "(*pf)(double)" declares a pointer to function
// taking double and returning int
int (*(*var1)(double))[3] = nullptr;
// decl-specifier-seq is "int"
// declarator is "(*(*var1)(double))[3]"
// initializer is "= nullptr"
// 1. declarator "(*(*var1)(double))[3]" is an array declarator:
// Type declared is: "(*(*var1)(double))" array of 3 elements
// 2. declarator "(*(*var1)(double))" is a pointer declarator:
// Type declared is: "(*var1)(double)" pointer to array of 3 elements
// 3. declarator "(*var1)(double)" is a function declarator:
// Type declared is: "(*var1)" function taking "(double)",
// returning pointer to array of 3 elements.
// 4. declarator "(*var1)" is a pointer declarator:
// Type declared is: "var1" pointer to function taking "(double)",
// returning pointer to array of 3 elements.
// 5. declarator "var1" is an identifier.
// This declaration declares the object var1 of type "pointer to function
// taking double and returning pointer to array of 3 elements of type int"
// The initializer "= nullptr" provides the initial value of this pointer.
// C++11 alternative syntax:
auto (*var2)(double) -> int (*)[3] = nullptr;
// decl-specifier-seq is "auto"
// declarator is "(*var2)(double) -> int (*)[3]"
// initializer is "= nullptr"
// 1. declarator "(*var2)(double) -> int (*)[3]" is a function declarator:
// Type declared is: "(*var2)" function taking "(double)", returning "int (*)[3]"
// ...
int main()
{
static_assert(std::is_same_v<decltype(var1), decltype(var2)>);
}
Отчёты о дефектах
Следующие отчёты о дефектах, изменяющих поведение, были применены ретроактивно к ранее опубликованным стандартам C++.