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 объявление
  • Объявление неполного перечисления
(начиная с C++11)
  • простое объявление

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

template <class T>
struct A {};

В этом примере объявлением, вводящим имя A, является спецификатор класса, тогда как объявлением, вводящим сущность — шаблон класса A, является окружающее его объявление шаблона.

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

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

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


init-declarator-list можно опустить только при объявлении именованного класса или именованного перечисления.

Объявление структурированной привязки также является простым объявлением.

(начиная с C++17)


Синтаксис init-declarator определяется следующим образом:

declarator initializer (1)
declarator requires-clause (необязательно) contract-specs (необязательно) (2)
1) Декларатор с инициализатором.
2) Декларатор без инициализатора.
declarator - декларатор
initializer - инициализатор
requires-clause - (начиная с C++20) предложение requires
contract-specs - (начиная с C++26) список спецификаторов контрактов функции

requires-clause может присутствовать, только если declarator объявляет шаблонную функцию.

(начиная с C++20)

contract-specs может присутствовать, только если declarator объявляет функцию или шаблон функции.

(начиная с C++26)

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

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

  • спецификатор typedef. Если он присутствует, всё объявление является объявлением typedef, и каждый декларатор вводит новое имя типа, а не объект или функцию.
  • спецификаторы функций (inline, virtual, explicit); допускаются только в объявлениях функций.
  • спецификатор inline также допускается в объявлениях переменных.
(начиная с C++17)
  • спецификатор friend, допустимый в объявлениях классов и функций.
  • спецификатор constexpr, допустимый только в определениях переменных, объявлениях функций и шаблонов функций, а также в объявлениях статических элементов данных литерального типа.
(начиная с C++11)
  • спецификатор consteval, допустимый только в объявлениях функций и шаблонов функций.
  • спецификатор constinit, допустимый только в объявлении переменной со статической длительностью хранения или длительностью хранения потока. В decl-specifier-seq может присутствовать не более одного из спецификаторов constexpr, consteval и constinit.
(начиная с 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)
  • спецификатор индексирования пакета
  • спецификатор типа splice
(начиная с C++26)
  • ранее объявленное имя класса (возможно, квалифицированное)
  • ранее объявленное имя перечисления (возможно, квалифицированное)
  • ранее объявленное имя typedef или псевдоним типа(начиная с C++11) (возможно, квалифицированное)
  • имя шаблона с аргументами шаблона (возможно, квалифицированное, возможно, с использованием дизамбигуатора template)
  • имя шаблона без аргументов шаблона (возможно, квалифицированное): см. выведение аргументов шаблона класса
(начиная с C++17)
  • уточнённый спецификатор типа
    • ключевое слово class, struct или union, за которым следует идентификатор (возможно, квалифицированный), ранее определённый как имя класса.
    • ключевое слово class, struct или union, за которым следует имя шаблона с аргументами шаблона (возможно, квалифицированное, возможно, с использованием template дизамбигуатора), ранее определённое как имя шаблона класса.
    • ключевое слово 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, например conststaticconst или virtualinlinevirtual, является ошибкой, за исключением того, что long может встречаться дважды(начиная с C++11).

Деклараторы

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

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

declarator — это один из следующих вариантов:

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, каждый из которых может встречаться в ней не более одного раза.

В соответствии с правилами поиска имён, если в одной области видимости объявлены тип и другая сущность с одинаковым именем, имя типа скрывается. Обратите внимание, что тип всё ещё можно найти с помощью уточнённого спецификатора типа.

class Foo {};
void Foo();
// Foo obj;// Error: 'Foo' is not a type
class Foo obj;// OK

Примечания

Если 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/cpp/language/declarations

Spec-Zone.ru

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