Spec-Zone.ru › C++

Объявления

Объявления — это способ ввода (или повторного ввода) имён в программу C++. Не все объявления фактически объявляют что-либо, и каждый вид сущности объявляется по-разному. Определения — это объявления, достаточные для использования сущности, идентифицируемой именем.

Объявление может быть одним из следующих:

  • Определение функции
  • Объявление шаблона (включая частичную специализацию шаблона)
  • Явную инстанциацию шаблона
  • Явную специализацию шаблона
  • Определение пространства имён
  • Спецификацию связи
  • Объявление атрибута (attr ;)
(с C++11)
  • Пустое объявление (;)
  • Объявление функции без decl-specifier-seq:
attr (необязательно) declarator ;
attr - (с C++11) последовательность любого количества атрибутов
declarator - декларатор функции
Эта декларация должна объявлять конструктор, деструктор или функцию преобразования пользовательского типа преобразования. Она может использоваться только в составе объявления шаблона, явной специализации или явной инстанциации.
  • block-declaration (объявление, которое может появиться внутри блока), которое, в свою очередь, может быть одним из следующих:
    • объявление asm
  • объявление псевдонима типа
(с C++11)
  • определение псевдонима пространства имён
  • объявление using
  • директива using
  • объявление using-enum
(с C++20)
  • static_assert объявление
  • объявление перечисления opaque
(с C++11)
  • простое объявление

Простое объявление

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

decl-specifier-seq init-declarator-list (необязательно) ; (1)
attr decl-specifier-seq init-declarator-list; (2)
attr - (с C++11) последовательность любого количества атрибутов
decl-specifier-seq - последовательность спецификаторов (см. ниже)
init-declarator-list - список деклараторов, разделённых запятыми, с необязательными инициализаторами. init-declarator-list необязательно при объявлении именованного класса/структуры/объединения или именованного перечисления

Объявление связанных структур также является простым объявлением.(с C++17)

Спецификаторы

Спецификаторы объявления (decl-specifier-seq) — это последовательность следующих разделенных пробелами спецификаторов в любом порядке:

  • спецификатор typedef. Если он присутствует, всё объявление является объявлением typedef, и каждый декларатор вводит новое имя типа, а не объект или функцию.
  • спецификаторы функций (inline, virtual, explicit), разрешены только в объявлениях функций.
  • спецификатор inline также разрешен в объявлениях переменных.
(с C++17)
  • спецификатор friend, разрешён в объявлениях классов и функций.
  • спецификатор constexpr, разрешен только в определениях переменных, объявлениях функций и шаблонных функций, и в объявлении статических данных-членов литерального типа.
(с C++11)
  • спецификатор consteval, разрешен только в объявлениях функций и шаблонных функций.
  • спецификатор constinit, разрешен только в объявлениях переменных со статической или потоковой областью хранения. Только один из спецификаторов constexpr, consteval, и constinit может присутствовать в decl-specifier-seq.
(с C++20)
  • спецификатор класса хранения (register, (до C++17) static, thread_local, (с C++11) extern, mutable). Разрешен только один спецификатор класса хранения, за исключением случаев, когда thread_local может появляться вместе с extern или static(с C++11).
  • Спецификаторы типов (type-specifier-seq), последовательность спецификаторов, которые называют тип. Тип каждого объекта, введённого в объявление, является этим типом, необязательно изменённым декларатором (см. ниже). Эта последовательность спецификаторов также используется в type-id. Только следующие спецификаторы являются частью type-specifier-seq в любом порядке:
  • спецификатор класса
  • спецификатор перечисления
  • простой спецификатор типа
    • char, char8_t, (с C++20) char16_t, char32_t, (с C++11) wchar_t, bool, short, int, long, signed, unsigned, float, double, void
  • auto
  • спецификатор decltype
(с C++11)
  • спецификатор pack indexing
(с C++26)
  • ранее объявленное имя класса (необязательно квалифицированное)
  • ранее объявленное имя перечисления (необязательно квалифицированное)
  • ранее объявленное имя typedef или псевдоним типа(с C++11) (необязательно квалифицированное)
  • имя шаблона с аргументами шаблона (необязательно квалифицированное, необязательно с использованием дизъюнктора шаблона)
  • имя шаблона без аргументов шаблона (необязательно квалифицированное): см. вывод аргументов шаблона класса
(с C++17)
  • расширенный спецификатор типа
    • ключевое слово class, struct или union, за которым следует идентификатор (необязательно квалифицированный), ранее определённый как имя класса, структуры или объединения.
    • ключевое слово class, struct или union, за которым следует имя шаблона с аргументами шаблона (необязательно квалифицированное, необязательно с использованием дизъюнктора шаблона), ранее определённое как имя шаблона класса.
    • ключевое слово enum, за которым следует идентификатор (необязательно квалифицированный), ранее объявленный как имя перечисления.
  • спецификатор typename
  • квалификатор cv
разрешено только одно указание типа в 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, разделённых запятыми, имеющая следующий синтаксис:

declarator initializer (необязательно) (1)
declarator requires-clause (2) (с C++20)
declarator - декларатор
initializer - необязательная инициализация (кроме случаев, когда она требуется, например, при инициализации ссылок или константных объектов). Подробности см. в Инициализации.
requires-clause - requires-оператор, который добавляет ограничение к объявлению функции

Каждый init-declarator в последовательности init-declarator обрабатывается так, как если бы это было самостоятельное объявление с теми же спецификаторами: S D1; S D2; S D3;.

Каждый декларатор вводит ровно один объект, ссылку, функцию или (для объявлений typedef) псевдоним типа, тип которого задаётся decl-specifier-seq и необязательно изменяется операторами, такими как & (ссылка на) или [] (массив из) или () (возвращающая функция) в деклараторе. Эти операторы могут применяться рекурсивно, как показано ниже.

Декларатор — это одно из следующего:

unqualified-id attr (необязательно) (1)
qualified-id attr (необязательно) (2)
... identifier attr (необязательно) (3) (с C++11)
* attr (необязательно) cv (необязательно) declarator (4)
nested-name-specifier * attr (необязательно) cv (необязательно) declarator (5)
& attr (необязательно) declarator (6)
&& attr (необязательно) declarator (7) (с C++11)
noptr-declarator [ constexpr (необязательно) ] attr (необязательно) (8)
noptr-declarator ( parameter-list ) cv (необязательно) ref  (необязательно) except (необязательно) attr (необязательно) (9)
1) Объявляемое имя.
2) Декларатор, использующий квалифицированное имя (qualified-id), определяет или повторно объявляет ранее объявленного члена пространства имён или члена класса.
3) Упаковку параметров, появляется только в объявлениях параметров.
4) Декларатор указателя: объявление S * D; объявляет D как указатель на тип, определяемый decl-specifier-seq S.
5) Декларатор указателя на член: объявление S C::* D; объявляет D как указатель на член C типа, определяемого decl-specifier-seq S. nested-name-specifier — последовательность имён и операторов разрешения области видимости ::
6) Декларатор ссылки на lvalue: объявление S & D; объявляет D как ссылку на lvalue на тип, определяемый decl-specifier-seq S.
7) Декларатор ссылки на rvalue: объявление S && D; объявляет D как ссылку на rvalue на тип, определяемый decl-specifier-seq S.
8) Декларатор массива. noptr-declarator любой допустимый декларатор, но если он начинается с *, &, или &&, он должен быть заключён в скобки.
9) Декларатор функции. noptr-declarator любой допустимый декларатор, но если он начинается с *, &, или &&, он должен быть заключён в скобки. Он может заканчиваться необязательным возвращаемым типом.(с C++11)

Во всех случаях, attr — это необязательная последовательность атрибутов. При появлении непосредственно после идентификатора, она применяется к объявляемому объекту.

(с C++11)

cv — последовательность квалификаторов const и volatile, где любой квалификатор может появиться не более одного раза в последовательности.

Примечания

Когда block-declaration появляется внутри блока, и идентификатор, введённый объявлением, был ранее объявлен во внешнем блоке, внешнее объявление скрывается до конца блока.

Если объявление вводит переменную с автоматическим сроком жизни, она инициализируется при выполнении её оператора объявления. Все автоматические переменные, объявленные в блоке, уничтожаются при выходе из блока (независимо от того, как блок покинут: через исключение, goto или достижением его конца), в порядке, обратном их порядку инициализации.

Пример

Примечание: этот пример демонстрирует, как некоторые сложные объявления разбираются с точки зрения грамматики языка. Другие популярные мнемоники: правило спирали, чтение изнутри наружу и объявление отражает использование. Также есть автоматизированный парсер по адресу https://cdecl.org.

#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++.

DR Применён к Поведение, как опубликовано Корректное поведение
CWG 482 C++98 деклараторы повторных объявлений не могли быть квалифицированы разрешены квалифицированные деклараторы
CWG 569 C++98 одиночная стоящая точка с запятой не была допустимым объявлением это пустое объявление,
которое не оказывает влияния
CWG 1830 C++98 повтор указания спецификатора функции в decl-specifier-seq допускался повтор запрещён

См. также

Документация C по Объявлениям

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/declarations

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API