10 Declarations [dcl.dcl]

Объявления обычно определяют, как следует интерпретировать имена. Объявления имеют форму

declaration-seq:
	declaration
	declaration-seq declaration
declaration:
	block-declaration
	nodeclspec-function-declaration
	function-definition
	template-declaration
	deduction-guide
	explicit-instantiation
	explicit-specialization
	linkage-specification
	namespace-definition
	empty-declaration
	attribute-declaration
block-declaration:
	simple-declaration
	asm-definition
	namespace-alias-definition
	using-declaration
	using-directive
	static_assert-declaration
	alias-declaration
	opaque-enum-declaration
nodeclspec-function-declaration:
	attribute-specifier-seqopt declarator ;
alias-declaration:
	using identifier attribute-specifier-seqopt = defining-type-id ;
simple-declaration:
	decl-specifier-seq init-declarator-listopt ;
	attribute-specifier-seq decl-specifier-seq init-declarator-list ;
	attribute-specifier-seqopt decl-specifier-seq ref-qualifieropt [ identifier-list ] initializer ;
static_assert-declaration:
	static_assert ( constant-expression ) ;
	static_assert ( constant-expression , string-literal ) ;
empty-declaration:
	;
attribute-declaration:
	attribute-specifier-seq ;

[ описаны в , и описаны в . описаны в и и описаны в п . описаны в , описаны и описаны в . ]Note: asm-definitions [dcl.asm]linkage-specifications [dcl.link]Function-definitions [dcl.fct.def] template-declarations deduction-guides [temp]Namespace-definitions [namespace.def]using-declarations [namespace.udecl] using-directives [namespace.udir]end note

A simple-declarationили nodeclspec-function-declarationформы

attribute-specifier-seqopt decl-specifier-seqopt init-declarator-listopt ;

делится на три части. Атрибуты описаны в [dcl.attr]. decl-specifiers, основные компоненты a decl-specifier-seq, описаны в [dcl.spec]. declarators, компоненты init-declarator-list, описанные в п [dcl.decl]. В attribute-specifier-seq appertains к каждому из лиц , заявленного заводом declarators из init-declarator-list. [ Note: В объявлении объекта атрибуты, относящиеся к этому объекту, могут появляться в начале объявления и после declarator-idэтого объявления. ] [end noteExample:

[[noreturn]] void f [[noreturn]] ();    // OK

end example]

Если не указано иное, значение параметра attribute-declaration определяется реализацией.

Объявление происходит в scope; правила области действия кратко изложены в [basic.lookup]. Объявление, которое объявляет функцию или определяет класс, пространство имен, шаблон или функцию, также имеет одну или несколько вложенных областей. Эти вложенные области, в свою очередь, могут иметь вложенные в них объявления. Если не указано иное, высказывания в Пункте [dcl.dcl] о компонентах, входящих в или содержащихся в объявлении или его подкомпоненте, относятся только к тем компонентам объявления, которые не вложены в области, вложенные в объявление.

В a simple-declarationнеобязательный параметр init-declarator-listможет быть опущен только при объявлении class или enumeration, то есть когда он decl-specifier-seqсодержит либо a class-specifier, либо elaborated-type-specifierс class-key([class.name]), либо enum-specifier. В этих случаях , и всякий раз , когда class-specifierили enum-specifierприсутствуют в decl-specifier-seqидентификаторах в этих спецификаторах являются одними из имен объявляются в декларации (как class-names, enum-namesили enumerators, в зависимости от синтаксиса). В таких случаях decl-specifier-seqдолжен ввести одно или несколько имен в программу или повторно объявить имя, введенное предыдущим объявлением. [Example:

enum { };           // ill-formed
typedef class { };  // ill-formed

end example]

В static_assert-declaration, то constant-expressionдолжно быть contextually converted constant expression of type bool. Если значение выражения при таком преобразовании равно true, объявление не действует. В противном случае программа имеет неправильный формат, и результирующее диагностическое сообщение ([intro.compliance]) должно включать текст string-literal, если таковой имеется, за исключением того, что символы, отсутствующие в basic source character set , не должны появляться в диагностическом сообщении. [Example:

static_assert(char(-1) < 0, "this library requires plain 'char' to be signed");

end example]

empty-declarationНе имеет никакого эффекта.

A simple-declarationс символом an identifier-listназывается a structured binding declaration ([dcl.struct.bind]). decl-specifier-seqДолжен содержать только type-specifier auto и cv-qualifiers. initializerДолжно иметь вид « », в форме « », или в форме « », где находится массив или не накидной типа класса.= assignment-expression{ assignment-expression }( assignment-expression )assignment-expression

Каждый init-declaratorв init-declarator-list содержит ровно одно declarator-id, которое является именем, объявленным этим, init-declaratorи, следовательно, одним из имен, объявленных в объявлении. defining-type-specifiers В decl-specifier-seqи рекурсивной declarator структуре init-declaratorописание типа ([dcl.meaning]), который затем связанный с именем объявляемого самыми init-declarator.

Если decl-specifier-seqсодержит typedef спецификатор, объявление называется a, typedef declaration а имя каждого init-declarator объявляется как a typedef-name, синоним связанного с ним type ([dcl.typedef]). Если decl-specifier-seqне содержит typedef спецификатора, объявление называется a, function declaration если тип, связанный с именем, является типом функции ([dcl.fct]), и в object declaration противном случае.

Синтаксические компоненты помимо тех, которые находятся в общей форме объявления, добавляются к объявлению функции для создания файла function-definition. Однако объявление объекта также является определением, если оно не содержит extern спецификатор и не имеет инициализатора ([basic.def]). Определение заставляет зарезервировать соответствующий объем памяти и выполнить любую соответствующую инициализацию ([dcl.init]).

A nodeclspec-function-declarationдолжен объявлять конструктор, деструктор или функцию преобразования.93 [ Может быть использован только в , или . ]Note: nodeclspec-function-declarationtemplate-declarationexplicit-instantiationexplicit-specializationend note

Правило C «неявных int» больше не поддерживается.

10.1 Specifiers [dcl.spec]

Спецификаторы, которые можно использовать в объявлении:

decl-specifier:
	storage-class-specifier
	defining-type-specifier
	function-specifier
	friend
	typedef
	constexpr
	inline
decl-specifier-seq:
	decl-specifier attribute-specifier-seqopt
	decl-specifier decl-specifier-seq

Необязательный параметр attribute-specifier-seqв a decl-specifier-seq относится к типу, определяемому предшествующим decl-specifiers ([dcl.meaning]). Параметр attribute-specifier-seq влияет на тип только для объявления, в котором он появляется, а не для других объявлений, связанных с тем же типом.

Каждый из них decl-specifierдолжен появляться не более одного раза в целом decl-specifier-seq, за исключением того, что long может появляться дважды.

Если type-nameвстречаются при синтаксическом анализе decl-specifier-seq, он интерпретируется как часть decl-specifier-seqтогда и только тогда , когда нет предыдущего defining-type-specifierдругой , чем cv-qualifierв decl-specifier-seq. Последовательность должна быть согласованной, как описано ниже. [Example:

typedef char* Pc;
static Pc;                      // error: name missing

Здесь объявление static Pc неправильно сформировано, потому что для статической переменной типа не указано имя Pc. Чтобы вызвать переменную Pc, должен присутствовать type-specifier(кроме const или volatile), чтобы указать, что typedef-name Pc это имя (повторно) объявляется, а не является частью decl-specifierпоследовательности. Другой пример:

void f(const Pc);               // void f(char* const) (not const char*)
void g(const int Pc);           // void g(const int)

end example]

[ Note: Так как signed, unsigned, long, и , short по умолчанию подразумевает int, A type-nameпоявляться после того, как один из этих спецификаторов трактуются как имя существа (ре) объявлено. [Example:

void h(unsigned Pc);            // void h(unsigned int)
void k(unsigned int Pc);        // void k(unsigned int)

end example] ]end note

10.1.1 Storage class specifiers [dcl.stc]

Спецификаторы класса хранения:

storage-class-specifier:
	static
	thread_local
	extern
	mutable

Максимум один storage-class-specifierдолжен появиться в данном задании decl-specifier-seq, за исключением того, что thread_­local может появиться с помощью static или extern. Если он thread_­local появляется в любом объявлении переменной, он должен присутствовать во всех объявлениях этого объекта. Если a storage-class-specifier появляется в a decl-specifier-seq, в нем не может быть typedef спецификатора, decl-specifier-seqи init-declarator-listor member-declarator-list объявления не должно быть пустым (за исключением анонимного объединения, объявленного в именованном пространстве имен или в глобальном пространстве имен, которое должно быть объявлено static ([class.union.anon])). storage-class-specifierОтносится к имени , объявленному каждому init-declaratorв списке , а не какие - либо имена , объявленных другими спецификаторами. storage-class-specifier Кроме thread_­local не должно быть указано в explicit specialization или в explicit instantiation директиве.

[ Note: Переменная, объявленная без storage-class-specifier области действия блока или объявленная как параметр функции, automatic storage duration по умолчанию. ]end note

Спецификатор thread_­local указывает, что названный объект имеет thread storage duration. Он должен применяться только к именам переменных пространства имен или области блока и к именам статических элементов данных. Когда thread_­local применяется к переменной области блока storage-class-specifier static , подразумевается, если storage-class-specifierв decl-specifier-seq.

Спецификатор static может применяться только к именам переменных и функций и к anonymous unions. Внутриstatic блока не может быть ни объявлений функций, ни каких-либо static параметров функции. static Спецификатор используется в объявлении переменной объявляет переменную , чтобы static storage duration, если не будет сопровождаться thread_­local спецификатора, который объявляет переменную иметь thread storage duration. static Спецификатор можно использовать в объявлениях членов класса; [class.static] описывает его эффект. О связи имени, объявленного с помощью static спецификатора, см [basic.link].

Спецификатор extern можно применять только к именам переменных и функций. Спецификатор extern нельзя использовать в объявлении членов класса или параметров функции. О связи имени, объявленного с помощью спецификатора, см . [ Ключевое слово также может быть использовано в и , но это не в таких условиях. ] extern [basic.link]Note: extern explicit-instantiations linkage-specificationsstorage-class-specifierend note

Связи, подразумеваемые последовательными декларациями для данного объекта, должны быть согласованы. То есть в пределах заданной области каждое объявление, объявляющее одно и то же имя переменной или одну и ту же перегрузку имени функции, должно подразумевать одну и ту же связь. Однако каждая функция в данном наборе перегруженных функций может иметь разные связи. [Example:

static char* f();               // f() has internal linkage
char* f()                       // f() still has internal linkage
  { /* ... */ }

char* g();                      // g() has external linkage
static char* g()                // error: inconsistent linkage
  { /* ... */ }

void h();
inline void h();                // external linkage

inline void l();
void l();                       // external linkage

inline void m();
extern void m();                // external linkage

static void n();
inline void n();                // internal linkage

static int a;                   // a has internal linkage
int a;                          // error: two definitions

static int b;                   // b has internal linkage
extern int b;                   // b still has internal linkage

int c;                          // c has external linkage
static int c;                   // error: inconsistent linkage

extern int d;                   // d has external linkage
static int d;                   // error: inconsistent linkage

end example]

В объявлении можно использовать имя объявленного, но неопределенного класса extern . Такое объявление может использоваться только способами, не требующими полного типа класса. [Example:

struct S;
extern S a;
extern S f();
extern void g(S);

void h() {
  g(a);                         // error: S is incomplete
  f();                          // error: S is incomplete
}

end example]

Спецификатор mutable должен появляться только в объявлении объекта non-static data member , тип которого не является ни константным, ни ссылочным типом. [Example:

class X {
  mutable const int* p;         // OK
  mutable int* const q;         // ill-formed
};

end example]

Спецификатор mutable члена данных класса обнуляет const спецификатор, примененный к содержащему объекту классу, и разрешает модификацию изменяемого члена класса, даже если остальной частью объекта является const ([dcl.type.cv]).

10.1.2 Function specifiers [dcl.fct.spec]

Function-specifiers может использоваться только в объявлениях функций.

function-specifier:
	virtual
	explicit

Спецификатор virtual должен использоваться только в начальном объявлении нестатической функции-члена класса; см [class.virtual].

Спецификатор explicit должен использоваться только в объявлении конструктора или функции преобразования в определении его класса; видеть [class.conv.ctor] и [class.conv.fct].

10.1.3 The typedef specifier [dcl.typedef]

Объявления, содержащие decl-specifier typedef идентификаторы объявления, которые можно использовать позже для именования fundamental или compound типов. Спецификатор typedef не должен сочетаться в a decl-specifier-seqс любым другим типом спецификатора, кроме a defining-type-specifier, и он не должен использоваться ни в decl-specifier-seqa, parameter-declarationни в decl-specifier-seqa function-definition([dcl.fct.def]). Если typedef спецификатор появляется в объявлении без символа declarator, программа имеет неправильный формат.

typedef-name:
	identifier

Имя, объявленное с помощью typedef спецификатора, становится typedef-name. В рамках своего объявления a typedef-nameсинтаксически эквивалентен ключевому слову и называет тип, связанный с идентификатором, способом, описанным в пункте [dcl.decl]. Таким образом, А является синонимом другого типа. A не вводит новый тип, как это делает объявление класса ( ) или объявление перечисления. [ Послеtypedef-nametypedef-name[class.name]Example:

typedef int MILES, *KLICKSP;

конструкции

MILES distance;
extern KLICKSP metricp;

все правильные объявления; тип distance is int и тип metricp is «указатель на int». ]end example

A typedef-nameтакже может быть введено через alias-declaration. identifierВслед за using ключевым словом становится typedef-name и опциональным attribute-specifier-seqследуя identifierвходит в этот typedef-name. Такой объект typedef-nameимеет такую ​​же семантику, как если бы он был введен typedef спецификатором. В частности, он не определяет новый тип. [Example:

using handler_t = void (*)(int);
extern handler_t ignore;
extern void (*ignore)(int);         // redeclare ignore
using cell = pair<void*, cell*>;    // ill-formed

end example] defining-type-specifier-seq Из defining-type-idне определяет класс или перечисление , если alias-declaration это declarationиз template-declaration.

В данной области, не относящейся к классу, typedef спецификатор может использоваться для переопределения имени любого типа, объявленного в этой области, для ссылки на тип, на который он уже ссылается. [Example:

typedef struct s { /* ... */ } s;
typedef int I;
typedef int I;
typedef I I;

end example]

В заданной области действия класса typedef спецификатор может использоваться для переопределения любого class-nameобъявленного в этой области действия, который не является также typedef-nameссылкой на тип, на который он уже ссылается. [Example:

struct S {
  typedef struct A { } A;       // OK
  typedef struct B B;           // OK
  typedef A A;                  // error
};

end example]

Если typedef спецификатор используется для переопределения в данной области сущности, на которую можно ссылаться с помощью elaborated-type-specifier, на эту сущность можно продолжать ссылаться с помощью elaborated-type-specifierили как перечисление или имя класса в перечислении или определении класса соответственно. [Example:

struct S;
typedef struct S S;
int main() {
  struct S* p;                  // OK
}
struct S { };                   // OK

end example]

В данной области typedef спецификатор не должен использоваться для переопределения имени любого типа, объявленного в этой области, для ссылки на другой тип. [Example:

class complex { /* ... */ };
typedef int complex;            // error: redefinition

end example]

Точно так же в данной области действия класс или перечисление не должны объявляться с тем же именем, что и объект typedef-name, объявленный в этой области, и относится к типу, отличному от самого класса или перечисления. [Example:

typedef int complex;
class complex { /* ... */ };    // error: redefinition

end example]

[ Имя типа класса или его cv-квалифицированная версия также является ( ). Если a используется для идентификации объекта , определения, объявления или объявления, программа имеет неправильный формат . ] [Note: typedef-nameclass-name[class.name]typedef-nameelaborated-type-specifier class constructor destructor end noteExample:

struct S {
  S();
  ~S();
};

typedef struct S T;

S a = T();                      // OK
struct T * p;                   // error

end example]

Если объявление typedef определяет безымянный класс (или перечисление), первый typedef-nameобъявленный в объявлении тип этого класса (или тип перечисления) используется для обозначения типа класса (или типа перечисления) только для целей связывания ([basic.link]). [Example:

typedef struct { } *ps, S;      // S is the class name for linkage purposes

end example]

10.1.4 The friend specifier [dcl.friend]

Спецификатор friend используется для указания доступа к членам класса; см [class.friend].

10.1.5 The constexpr specifier [dcl.constexpr]

Спецификатор constexpr должен применяться только к определению переменной или шаблона переменной или к объявлению функции или шаблона функции. Функция или статический член данных, объявленный с помощью constexpr спецификатора, неявно является встроенной функцией или переменной ([dcl.inline]). Если какое-либо объявление функции или шаблона функции имеет constexpr спецификатор, то все его объявления должны содержать constexpr спецификатор. [ Note: Явная специализация может отличаться от объявления шаблона в отношении constexpr спецификатора. ] [ Параметры функции не могут быть объявлены . ] [ end noteNote: constexprend noteExample:

constexpr void square(int &x);  // OK: declaration
constexpr int bufsz = 1024;     // OK: definition
constexpr struct pixel {        // error: pixel is a type
  int x;
  int y;
  constexpr pixel(int);         // OK: declaration
};
constexpr pixel::pixel(int a)
  : x(a), y(x)                  // OK: definition
  { square(x); }
constexpr pixel small(2);       // error: square not defined, so small(2)
                                // not constant ([expr.const]) so constexpr not satisfied

constexpr void square(int &x) { // OK: definition
  x *= x;
}
constexpr pixel large(4);       // OK: square defined
int next(constexpr int x) {     // error: not for parameters
     return x + 1;
}
extern constexpr int memsz;     // error: not a definition

end example]

constexpr Спецификатор используется в объявлении функции, не конструктор заявляет , что функция будет constexpr function. Точно так же constexpr спецификатор, используемый в объявлении конструктора, объявляет, что этот конструктор является constexpr constructor.

Определение функции constexpr должно удовлетворять следующим требованиям:

  • не должно быть virtual;

  • его возвращаемый тип должен быть буквальным;

  • каждый из его типов параметров должен быть буквальным типом;

  • это function-bodyдолжно быть = delete, = defaultили, compound-statement которое не содержит

    • ан asm-definition,

    • goto заявление,

    • ан identifier label,

    • а try-block, или

    • определение переменной нелитерального типа, либо статической, либо продолжительности хранения потока, либо для которой не выполняется инициализация.

[Example:

constexpr int square(int x)
  { return x * x; }             // OK
constexpr long long_max()
  { return 2147483647; }        // OK
constexpr int abs(int x) {
  if (x < 0)
    x = -x;
  return x;                     // OK
}
constexpr int first(int n) {
  static int value = n;         // error: variable has static storage duration
  return value;
}
constexpr int uninit() {
  int a;                        // error: variable is uninitialized
  return a;
}
constexpr int prev(int x)
  { return --x; }               // OK
constexpr int g(int x, int n) { // OK
  int r = 1;
  while (--n > 0) r *= x;
  return r;
}

end example]

Определение конструктора constexpr должно удовлетворять следующим требованиям:

  • у класса не должно быть никаких виртуальных базовых классов;

  • каждый из типов параметров должен быть буквальным типом;

  • его function-bodyне должно быть function-try-block.

Кроме того, он либо function-bodyдолжен быть = delete, либо удовлетворять следующим требованиям:

  • либо его function-bodyдолжно быть = default, либо compound-statementего function-body должно удовлетворять требованиям для function-bodyфункции constexpr;

  • каждый невариантный нестатический член данных и подобъект базового класса должен быть инициализирован ([class.base.init]);

  • если класс является объединением, имеющим вариантные члены ([class.union]), должен быть инициализирован ровно один из них;

  • если класс является подобным объединению классом, но не является объединением, для каждого из его анонимных членов объединения, имеющих вариантные члены, должен быть инициализирован ровно один из них;

  • для конструктора без делегирования каждый конструктор, выбранный для инициализации нестатических членов данных и подобъектов базового класса, должен быть конструктором constexpr;

  • для делегирующего конструктора целевой конструктор должен быть конструктором constexpr.

[Example:

struct Length {
  constexpr explicit Length(int i = 0) : val(i) { }
private:
  int val;
};

end example]

Для функции constexpr или конструктора constexpr, который не является ни заданным по умолчанию, ни шаблоном, если не существует таких значений аргументов, что вызов функции или конструктора мог быть вычисленным подвыражением a core constant expression, или, для конструктора, константным инициализатором для некоторого объекта ([basic.start.static]), программа некорректна, диагностика не требуется. [Example:

constexpr int f(bool b)
  { return b ? throw 0 : 0; }           // OK
constexpr int f() { return f(true); }   // ill-formed, no diagnostic required

struct B {
  constexpr B(int x) : i(0) { }         // x is unused
  int i;
};

int global;

struct D : B {
  constexpr D() : B(global) { }         // ill-formed, no diagnostic required
                                        // lvalue-to-rvalue conversion on non-constant global
};

end example]

Если конкретизированная специализация шаблона шаблона функции constexpr или функции-члена шаблона класса не сможет удовлетворить требования для функции constexpr или конструктора constexpr, эта специализация по-прежнему является функцией constexpr или конструктором constexpr, даже если вызов такой функции не может присутствовать в постоянном выражении. Если никакая специализация шаблона не удовлетворяет требованиям для функции constexpr или конструктора constexpr, если рассматривать его как функцию или конструктор, не являющиеся шаблоном, шаблон сформирован неправильно, диагностика не требуется.

Вызов функции constexpr дает тот же результат, что и вызов эквивалентной функции non-constexpr во всех отношениях, за исключением того, что

  • вызов функции constexpr может появиться в constant expression и

  • copy elision является обязательным в постоянном выражении ([class.copy]).

Спецификатор constexpr не влияет на тип функции constexpr или конструктора constexpr. [Example:

constexpr int bar(int x, int y)         // OK
    { return x + y + x*y; }
// ...
int bar(int x, int y)                   // error: redefinition of bar
    { return x * 2 + 3 * y; }

end example]

constexpr Спецификатор используется в объявлении объекта объявляет объект как const. Такой объект должен иметь буквальный тип и быть инициализирован. В любом constexpr объявлении переменной полное выражение инициализации должно быть constant expression. [Example:

struct pixel {
  int x, y;
};
constexpr pixel ur = { 1294, 1024 };    // OK
constexpr pixel origin;                 // error: initializer missing

end example]

10.1.6 The inline specifier [dcl.inline]

Спецификатор inline может применяться только к объявлению или определению переменной или функции.

Объявление функции ([dcl.fct], [class.mfct], [class.friend]) с inline спецификатором объявляет какойinline function. Встроенный спецификатор указывает реализации, что встроенная подстановка тела функции в точке вызова предпочтительнее обычного механизма вызова функции. Реализация не требуется для выполнения этой встроенной замены в точке вызова; однако, даже если эта встроенная подстановка опущена, все равно должны соблюдаться другие правила для встроенных функций, указанные в этом разделе.

Объявление переменной со inline спецификатором объявляет inline variable.

Функция, определенная в определении класса, является встроенной функцией.

Спецификатор inline не должен появляться в объявлении области действия блока.94 Если inline спецификатор используется в объявлении дружественной функции, это объявление должно быть определением, или функция должна быть ранее объявлена ​​встроенной.

Встроенная функция или переменная должна быть определена в каждой единице перевода, в которой она используется odr, и должна иметь точно такое же определение во всех случаях ([basic.def.odr]). [ Note: Вызов встроенной функции или использование встроенной переменной может встречаться до того, как ее определение появится в блоке перевода. ] Если определение функции или переменной появляется в единице перевода перед ее первым объявлением как встроенное, программа имеет неправильный формат. Если функция или переменная с внешней связью объявляется встроенной в одной единице перевода, она должна быть объявлена ​​встроенной во всех единицах перевода, в которых она появляется; Диагностика не требуется. Встроенная функция или переменная с внешней связью должна иметь один и тот же адрес во всех единицах трансляции. [ Локальная переменная в инлайн функции с внешним связыванием всегда относится к тому же объекту. Тип, определенный в теле встроенной функции с внешней связью, является одним и тем же типом во всех единицах перевода. ]end noteNote: static end note

inline Ключевое слово не имеет никакого влияния на связь функции.

10.1.7 Type specifiers [dcl.type]

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

type-specifier:
	simple-type-specifier
	elaborated-type-specifier
	typename-specifier
	cv-qualifier
type-specifier-seq:
	type-specifier attribute-specifier-seqopt
	type-specifier type-specifier-seq
defining-type-specifier:
	type-specifier
	class-specifier
	enum-specifier
defining-type-specifier-seq:
	defining-type-specifier attribute-specifier-seqopt
	defining-type-specifier defining-type-specifier-seq

Необязательное attribute-specifier-seqв a type-specifier-seq или a defining-type-specifier-seq относится к типу, обозначенному предыдущим символом type-specifiers или defining-type-specifiers ([dcl.meaning]). Параметр attribute-specifier-seqвлияет на тип только для объявления, в котором он появляется, а не для других объявлений, связанных с тем же типом.

Как правило, defining-type-specifier разрешается не более одного в полном decl-specifier-seqa declarationили в a defining-type-specifier-seq, и не более одного type-specifier разрешается в a type-specifier-seq. Единственными исключениями из этого правила являются следующие:

  • const может сочетаться с любым спецификатором типа, кроме самого себя.

  • volatile может сочетаться с любым спецификатором типа, кроме самого себя.

  • signed или unsigned могут быть объединены с char, long, shortили int.

  • short или long можно комбинировать с int.

  • long можно комбинировать с double.

  • long можно комбинировать с long.

За исключением объявления конструктора, деструктора или функции преобразования, по крайней мере один, defining-type-specifierкоторый не является, cv-qualifierдолжен появиться в завершенном type-specifier-seqили завершенном виде decl-specifier-seq.95

[ , И обсуждаются в , п , и , соответственно. Остальные обсуждаются в оставшейся части этого раздела. ]Note: enum-specifiersclass-specifierstypename-specifiers[dcl.enum] [class][temp.res]type-specifiers end note

Не существует специального положения для a, в decl-specifier-seqкотором отсутствует a type-specifierили для a, type-specifierкоторый только указывает cv-qualifiers. Правило C «неявных int» больше не поддерживается.

10.1.7.1 The cv-qualifiers [dcl.type.cv]

Их два cv-qualifiers, const и volatile. Каждый cv-qualifierдолжен появляться не более одного раза в cv-qualifier-seq. Если a cv-qualifierпоявляется в a decl-specifier-seq, то init-declarator-list или member-declarator-listдекларации не может быть пустым. [ Note: [basic.type.qualifier] и [dcl.fct] опишите, как cv-квалификаторы влияют на типы объектов и функций. ] Избыточные CV-квалификации игнорируются. [ Например, они могут быть введены с помощью typedefs. ]end noteNote: end note

[ Note: Объявление переменной const может повлиять на ее linkage ([dcl.stc]) и удобство использования в constant expressions. Как описано в [dcl.init], определение объекта или подобъекта типа с указанием const должно указывать инициализатор или подвергаться инициализации по умолчанию. ]end note

Указатель или ссылка на cv-квалифицированный тип не обязательно должны указывать или ссылаться на cv-квалифицированный объект, но он обрабатывается так, как если бы он это делал; Путь доступа с указанием констант не может использоваться для изменения объекта, даже если указанный объект не является константным объектом и может быть изменен с помощью другого пути доступа. [ Note: Cv-квалификаторы поддерживаются системой типов, так что без них их нельзя ниспровергнуть casting. ]end note

За исключением того, что любой объявленный член класса mutable может быть изменен, любая попытка изменить const объект во время его выполнения lifetime приводит к неопределенному поведению. [Example:

const int ci = 3;                       // cv-qualified (initialized as required)
ci = 4;                                 // ill-formed: attempt to modify const

int i = 2;                              // not cv-qualified
const int* cip;                         // pointer to const int
cip = &i;                               // OK: cv-qualified access path to unqualified
*cip = 4;                               // ill-formed: attempt to modify through ptr to const

int* ip;
ip = const_cast<int*>(cip);             // cast needed to convert const int* to int*
*ip = 4;                                // defined: *ip points to i, a non-const object

const int* ciq = new const int (3);     // initialized as required
int* iq = const_cast<int*>(ciq);        // cast required
*iq = 4;                                // undefined: modifies a const object

Другой пример:

struct X {
  mutable int i;
  int j;
};
struct Y {
  X x;
  Y();
};

const Y y;
y.x.i++;                                // well-formed: mutable member can be modified
y.x.j++;                                // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y);              // cast away const-ness of y
p->x.i = 99;                            // well-formed: mutable member can be modified
p->x.j = 99;                            // undefined: modifies a const member

end example]

Семантика доступа через изменчивое значение glvalue определяется реализацией. Если предпринята попытка доступа к объекту, определенному с типом с изменяемым атрибутом, с помощью энергонезависимого значения glvalue, поведение не определено.

[ Note: volatile является подсказкой реализации, чтобы избежать агрессивной оптимизации, связанной с объектом, потому что значение объекта может быть изменено средствами, не обнаруживаемыми реализацией. Кроме того, для некоторых реализаций volatile может указывать на то, что для доступа к объекту требуются специальные аппаратные инструкции. См. [intro.execution] Подробную семантику. В общем, семантика volatile в C ++ должна быть такой же, как и в C. ]end note

10.1.7.2 Simple type specifiers [dcl.type.simple]

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

simple-type-specifier:
	nested-name-specifieropt type-name
	nested-name-specifier template simple-template-id
	nested-name-specifieropt template-name
	char
	char16_t
	char32_t
	wchar_t
	bool
	short
	int
	long
	signed
	unsigned
	float
	double
	void
	auto
	decltype-specifier
type-name:
	class-name
	enum-name
	typedef-name
	simple-template-id
decltype-specifier:
	decltype ( expression )
	decltype ( auto )

Это simple-type-specifier auto заполнитель для выводимого типа ([dcl.spec.auto]). A type-specifierформы является заполнителем для выведенного типа класса ( ). Он должен называть шаблон класса, который не является внедренным именем класса. Другой указывает либо ранее объявленный тип, тип, определяемый выражением, либо один из . В таблице приведены допустимые комбинации и указанные ими типы.typenameopt nested-name-specifieropt template-name[dcl.type.class.deduct]template-namesimple-type-specifiersfundamental types 11simple-type-specifiers

Таблица 11 - simple-type-specifiers и типы, которые они указывают
Спецификатор (ы) Тип
type-name названный тип
simple-template-id тип, как определено в [temp.names]
template-name заполнитель для выводимого типа
символ «Чар»
беззнаковый символ «Беззнаковый символ»
подписанный символ «Подписанный символ»
char16_t «Char16_t»
char32_t «Char32_t»
bool «Булево»
беззнаковый «Беззнаковое целое»
беззнаковое целое «Беззнаковое целое»
подписано «Int»
подписанный int «Int»
int «Int»
беззнаковый короткий int «Короткое беззнаковое целое»
беззнаковый короткий «Короткое беззнаковое целое»
беззнаковое длинное целое «Беззнаковое длинное целое число»
беззнаковый длинный «Беззнаковое длинное целое число»
беззнаковый длинный длинный int «Беззнаковый длинный длинный int»
беззнаковый длинный длинный «Беззнаковый длинный длинный int»
подписанный длинный int «Длинный интервал»
подписан долго «Длинный интервал»
подписанный длинный длинный int «Длинный длинный интервал»
подписан долго «Длинный длинный интервал»
длинный длинный int «Длинный длинный интервал»
долго долго «Длинный длинный интервал»
длинный интервал «Длинный интервал»
длинный «Длинный интервал»
подписанный короткий int «Короткий интервал»
подписанный короткий «Короткий интервал»
короткий int «Короткий интервал»
короткая «Короткий интервал»
wchar_t «Wchar_t»
плавать "плавать"
двойной "двойной"
длинный двойной «Длинный дубль»
пустота "пустота"
авто заполнитель для выводимого типа
decltype (авто) заполнитель для выводимого типа
decltype (expression) тип, как определено ниже

Когда simple-type-specifiers разрешено несколько , они могут свободно смешиваться с другими decl-specifiers в любом порядке. [ Note: Это определяется реализацией, представлены ли объекты char типа в виде количества со знаком или без знака. Спецификатор signed заставляет char объекты подписываться; в других контекстах это избыточно. ]end note

Для выражения eтип, обозначенный decltype(e) как, определяется следующим образом:

  • if e - это имя без скобок id-expression для структурированной привязки ([dcl.struct.bind]), decltype(e) - это ссылочный тип, как указано в спецификации объявления структурированной привязки;

  • в противном случае, если e без скобок id-expressionили без скобок class member access, decltype(e) это тип сущности, названной по e. Если такой сущности нет или если она e называет набор перегруженных функций, программа плохо сформирована;

  • в противном случае, если e - значение x, decltype(e) это T&&, где T - тип e;

  • в противном случае, если e - lvalue, decltype(e) is T&, где T - тип e;

  • в противном случае decltype(e) - это тип e.

Операнд decltype спецификатора - это unevaluated operand.

[Example:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

end example] [ Note: Правила определения задействованных типов decltype(auto) указаны в [dcl.spec.auto]. ]end note

Если операндом a decltype-specifierявляется prvalue, то temporary materialization conversion не применяется и объект результата для prvalue не предоставляется. Тип prvalue может быть неполным. [ Note: В результате для prvalue память не выделяется и не уничтожается. Таким образом, тип класса не создается в результате того, что он является типом вызова функции в этом контексте. В этом контексте обычная цель написания выражения - просто сослаться на его тип. В этом смысле a decltype-specifierаналогичен использованию a typedef-name, поэтому обычные причины для требования полного типа не применяются. В частности, нет необходимости выделять память для временного объекта или принудительно применять семантические ограничения, связанные с вызовом деструктора типа. ] [ В отличие от предыдущего правила, круглые скобки не имеют особого значения в этом контексте. ] [end noteNote: end noteExample:

template<class T> struct A { ~A() = delete; };
template<class T> auto h()
  -> A<T>;
template<class T> auto i(T)     // identity
  -> T;
template<class T> auto f(T)     // #1
  -> decltype(i(h<T>()));       // forces completion of A<T> and implicitly uses A<T>​::​~A()
                                // for the temporary introduced by the use of h().
                                // (A temporary is not introduced as a result of the use of i().)
template<class T> auto f(T)     // #2
  -> void;
auto g() -> void {
  f(42);                        // OK: calls #2. (#1 is not a viable candidate: type deduction
                                // fails ([temp.deduct]) because A<int>​::​~A() is implicitly used in its
                                // decltype-specifier)
}
template<class T> auto q(T)
  -> decltype((h<T>()));        // does not force completion of A<T>; A<T>​::​~A() is not implicitly
                                // used within the context of this decltype-specifier
void r() {
  q(42);                        // Error: deduction against q succeeds, so overload resolution selects
                                // the specialization “q(T) -> decltype((h<T>())) [with T=int]”.
                                // The return type is A<int>, so a temporary is introduced and its
                                // destructor is used, so the program is ill-formed.
}

end example]

10.1.7.3 Elaborated type specifiers [dcl.type.elab]

elaborated-type-specifier:
	class-key attribute-specifier-seqopt nested-name-specifieropt identifier
	class-key simple-template-id
	class-key nested-name-specifier templateopt simple-template-id
	enum nested-name-specifieropt identifier

attribute-specifier-seqНе должно появляться в elaborated-type-specifier тех пор , пока последний не является единственным компонентом декларации. Если an elaborated-type-specifierявляется единственной составляющей декларации, декларация неправильно сформирована, если только она не является explicit specialization, an explicit instantiation или имеет одну из следующих форм:

class-key attribute-specifier-seqopt identifier ;
friend class-key ::opt identifier ;
friend class-key ::opt simple-template-id ;
friend class-key nested-name-specifier identifier ;
friend class-key nested-name-specifier templateopt simple-template-id ;

В первом случае, attribute-specifier-seqесли есть, принадлежит объявленному классу; атрибуты в attribute-specifier-seqвпоследствии считаются атрибутами класса, когда бы он ни был назван.

[basic.lookup.elab] описывает, как выполняется поиск имени identifierв файле elaborated-type-specifier. Если identifierразрешается в class-nameили enum-name, elaborated-type-specifier вводит его в объявление так же, как simple-type-specifierвводит свой type-name. Если identifierразрешается в typedef-nameили simple-template-idрешает шаблон псевдонима специализации, то elaborated-type-specifierплохо сформирован. [ Note: Это означает, что в шаблоне класса с шаблоном type-parameter Tобъявление

friend class T;

плохо сформирован. Однако подобное объявление friend T; разрешено ([class.friend]). ]end note

class-keyИли enum ключевое слово присутствует в elaborated-type-specifierсогласовывают в натуральной форме с декларацией , в которой имя в elaborated-type-specifierссылается. Это правило также применяется к форме elaborated-type-specifierобъявления класса class-nameили, friend поскольку оно может быть истолковано как относящееся к определению класса. Таким образом, в любом elaborated-type-specifier, то enum ключевое слово должно использоваться для обозначения enumeration, то должны быть использованы для обозначения , и либо или должны быть использованы для обозначения объявляются с помощью или . [ union class-key union class struct class-key class class struct class-keyExample:

enum class E { a, b };
enum E x = E::a;                // OK

end example]

10.1.7.4 The auto specifier [dcl.spec.auto]

Символы auto и используются для обозначения типа заполнителя, который позже будет заменен путем вычитания из инициализатора. Также используется , чтобы ввести тип функции , имеющий или чтобы показать , что лямбда является общим лямбда ( ). Также используется для введения . decltype(auto) type-specifiers auto type-specifiertrailing-return-type[expr.prim.lambda.closure] auto type-specifierstructured binding declaration

Тип заполнитель может появиться с функцией описателем в decl-specifier-seq, type-specifier-seq, conversion-function-id, или trailing-return-type, в любом контексте , где такой описатель является действительным. Если декларатор функции включает в себя trailing-return-type([dcl.fct]), который trailing-return-typeуказывает объявленный тип возвращаемого значения функции. В противном случае декларатор функции должен объявить функцию. Если объявленный тип возвращаемого значения функции содержит тип-заполнитель, тип возвращаемого значения функции выводится из неотброшенных return операторов, если таковые имеются, в теле функции ([stmt.if]).

Если появляется как один из в a из a , лямбда - это ( ). [ auto type-specifierdecl-specifiers decl-specifier-seqparameter-declarationlambda-expression generic lambda [expr.prim.lambda.closure]Example:

auto glambda = [](int i, auto a) { return i; };     // OK: a generic lambda

end example]

Тип переменной, объявленной с использованием auto или decltype(auto) , выводится из ее инициализатора. Это использование разрешено в инициализирующем объявлении ([dcl.init]) переменной. auto или decltype(auto) должно отображаться как одно из decl-specifiers в, decl-specifier-seqа за ним decl-specifier-seq должно следовать одно или несколько declarators, за каждым из которых должно следовать непустое значение initializer. В initializerформе

( expression-list )

это expression-listдолжно быть единым assignment-expression. [Example:

auto x = 5;                     // OK: x has type int
const auto *v = &x, u = 6;      // OK: v has type const int*, u has type const int
static auto y = 0.0;            // OK: y has type double
auto int r;                     // error: auto is not a storage-class-specifier
auto f() -> int;                // OK: f returns int
auto g() { return 0.0; }        // OK: g returns double
auto h();                       // OK: h's return type will be deduced when it is defined

end example]

Тип заполнитель также может быть использован в type-specifier-seqв new-type-idили type-idв А new-expression и , как decl-specifier из parameter-declarationdecl-specifier-seq в template-parameter.

Программа, которая использует auto или decltype(auto) в контексте, явно не разрешенном в этом разделе, имеет неправильный формат.

Если init-declarator-listсодержит более одного init-declarator, все они должны формировать объявления переменных. Тип каждой объявленной переменной определяется placeholder type deduction, и если тип, заменяющий тип заполнителя, не является одинаковым при каждом выводе, программа имеет неправильный формат.

[Example:

auto x = 5, *y = &x;            // OK: auto is int
auto a = 5, b = { 1, 2 };       // error: different types for auto

end example]

Если функция с объявленным типом возвращаемого значения, содержащим тип заполнителя, имеет несколько неотброшенных return операторов, тип возвращаемого значения выводится для каждого такого return оператора. Если выведенный тип не является одинаковым в каждом из выводов, программа сформирована неправильно.

Если функция с объявленным типом возврата, которая использует тип заполнителя, не имеет неотброшенных return операторов, тип возвращаемого значения выводится, как если бы из return оператора без операнда в закрывающей фигурной скобке тела функции. [Example:

auto  f() { }                   // OK, return type is void
auto* g() { }                   // error, cannot deduce auto* from void()

end example]

Если для определения типа выражения требуется тип сущности с невыявленным типом заполнителя, программа имеет неправильный формат. Однако после того, как неотброшенный return оператор был замечен в функции, тип возвращаемого значения, выведенный из этого оператора, можно использовать в остальной части функции, в том числе в других return операторах. [Example:

auto n = n;                     // error, n's type is unknown
auto f();
void g() { &f; }                // error, f's return type is unknown
auto sum(int i) {
  if (i == 1)
    return i;                   // sum's return type is int
  else
    return sum(i-1)+i;          // OK, sum's return type has been deduced
}

end example]

Вывод типа возвращаемого значения для шаблона функции с заполнителем в объявленном типе происходит при создании экземпляра определения, даже если тело функции содержит return оператор с операндом, не зависящим от типа. [ Note: Следовательно, любое использование специализации шаблона функции вызовет неявное создание экземпляра. Любые ошибки, возникающие из-за этого экземпляра, не относятся к непосредственному контексту типа функции и могут привести к неправильному формату программы ([temp.deduct]). ] [ end noteExample:

template <class T> auto f(T t) { return t; }    // return type deduced at instantiation time
typedef decltype(f(1)) fint_t;                  // instantiates f<int> to deduce return type
template<class T> auto f(T* t) { return *t; }
void g() { int (*p)(int*) = &f; }               // instantiates both fs to determine return types,
                                                // chooses second

end example]

Повторные объявления или специализации функции или шаблона функции с объявленным типом возврата, который использует тип заполнителя, также должны использовать этот заполнитель, а не выводимый тип. [Example:

auto f();
auto f() { return 42; }                         // return type is int
auto f();                                       // OK
int f();                                        // error, cannot be overloaded with auto f()
decltype(auto) f();                             // error, auto and decltype(auto) don't match

template <typename T> auto g(T t) { return t; } // #1
template auto g(int);                           // OK, return type is int
template char g(char);                          // error, no matching template
template<> auto g(double);                      // OK, forward declaration with unknown return type

template <class T> T g(T t) { return t; }       // OK, not functionally equivalent to #1
template char g(char);                          // OK, now there is a matching template
template auto g(float);                         // still matches #1

void h() { return g(42); }                      // error, ambiguous

template <typename T> struct A {
  friend T frf(T);
};
auto frf(int i) { return i; }                   // not a friend of A<int>

end example]

Функция, объявленная с возвращаемым типом, использующим тип-заполнитель, не должна быть virtual.

An explicit instantiation declaration не вызывает создание экземпляра сущности, объявленной с использованием типа заполнителя, но также не предотвращает создание экземпляра этой сущности по мере необходимости для определения ее типа. [Example:

template <typename T> auto f(T t) { return t; }
extern template auto f(int);    // does not instantiate f<int>
int (*p)(int) = f;              // instantiates f<int> to determine its return type, but an explicit
                                // instantiation definition is still required somewhere in the program

end example]

10.1.7.4.1 Placeholder type deduction [dcl.type.auto.deduct]

Placeholder type deduction - это процесс, с помощью которого тип, содержащий тип-заполнитель, заменяется выведенным типом.

Тип, T содержащий тип заполнителя и соответствующий инициализатор e, определяются следующим образом:

  • для неотброшенного return оператора, который встречается в функции, объявленной с возвращаемым типом, который содержит тип-заполнитель, T является объявленным возвращаемым типом и e является операндом return оператора. Если у return оператора нет операнда, то e есть void();

  • для переменной, объявленной с типом, содержащим тип-заполнитель, T является объявленным типом переменной и e инициализатором. Если инициализация - это инициализация с прямым списком, инициализатор должен braced-init-list содержать только один элемент assignment-expression и e является assignment-expression;

  • для параметра шаблона, не являющегося типом, объявленного с типом, содержащим тип-заполнитель, T является объявленным типом параметра шаблона, не являющимся типом, и e является соответствующим аргументом шаблона.

В случае return заявления, без операнда или с операндом типа void, T должны быть либо decltype(auto) или cv auto.

Если вывод предназначен для return оператора и e представляет собой braced-init-list([dcl.init.list]), программа сформирована неправильно.

Если заполнитель - это , замена выведенного типа определяется с использованием правил вывода аргументов шаблона. Получить от путем замены вхождений либо на новый параметр шаблона изобретенного типа, либо, если инициализация - инициализация списка-копирования, на . Выведите значение для использования правил , где - тип параметра шаблона функции, а соответствующий аргумент - . Если вычет не удается, декларация имеет неверный формат. В противном случае получается заменой выведенного на . [ auto type-specifierT' T P T auto U std​::​initializer_­list<U> U template argument deduction from a function call P e T' U PExample:

auto x1 = { 1, 2 };             // decltype(x1) is std​::​initializer_­list<int>
auto x2 = { 1, 2.0 };           // error: cannot deduce element type
auto x3{ 1, 2 };                // error: not a single element
auto x4 = { 3 };                // decltype(x4) is std​::​initializer_­list<int>
auto x5{ 3 };                   // decltype(x5) is int

end example]

[Example:

const auto &i = expr;

Тип i - это выведенный тип параметра u при вызове f(expr) следующего шаблона придуманной функции:

template <class U> void f(const U& u);

end example]

Если заполнителем является , должен быть только заполнитель. Тип, выведенный для , определяется, как описано в , как если бы он был операндом . [ decltype(auto) type-specifierT T [dcl.type.simple]e decltypeExample:

int i;
int&& f();
auto           x2a(i);          // decltype(x2a) is int
decltype(auto) x2d(i);          // decltype(x2d) is int
auto           x3a = i;         // decltype(x3a) is int
decltype(auto) x3d = i;         // decltype(x3d) is int
auto           x4a = (i);       // decltype(x4a) is int
decltype(auto) x4d = (i);       // decltype(x4d) is int&
auto           x5a = f();       // decltype(x5a) is int
decltype(auto) x5d = f();       // decltype(x5d) is int&&
auto           x6a = { 1, 2 };  // decltype(x6a) is std​::​initializer_­list<int>
decltype(auto) x6d = { 1, 2 };  // error, { 1, 2 } is not an expression
auto          *x7a = &i;        // decltype(x7a) is int*
decltype(auto)*x7d = &i;        // error, declared type is not plain decltype(auto)

end example]

10.1.7.5 Deduced class template specialization types [dcl.type.class.deduct]

Если заполнитель для выведенного типа класса отображается как a decl-specifier в decl-specifier-seq объявлении инициализации ([dcl.init]) переменной, заполнитель заменяется типом возвращаемого значения функции, выбранной разрешением перегрузки для вывода шаблона класса ([over.match.class.deduct]). Если за decl-specifier-seq символом следует init-declarator-list или, member-declarator-list содержащий более одного declarator, тип, который заменяет заполнитель, должен быть одинаковым при каждом вычитании.

Заполнитель для выведенного типа класса также может использоваться type-specifier-seq в new-type-idили type-id в new-expression, или как simple-type-specifier в явном преобразовании типа (функциональная нотация) ([expr.type.conv]). Заполнитель для выведенного типа класса не должен появляться ни в каком другом контексте.

[Example:

template<class T> struct container {
    container(T t) {}
    template<class Iter> container(Iter beg, Iter end);
};
template<class Iter>
container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
std::vector<double> v = { /* ... */ };

container c(7);                         // OK, deduces int for T
auto d = container(v.begin(), v.end()); // OK, deduces double for T
container e{5, 6};                      // error, int is not an iterator

end example]

10.2 Enumeration declarations [dcl.enum]

Перечисление - это отдельный тип ([basic.compound]) с именованными константами. Его имя становится enum-nameвнутри его объема.

enum-name:
	identifier
enum-specifier:
	enum-head { enumerator-listopt }
	enum-head { enumerator-list , }
enum-head:
	enum-key attribute-specifier-seqopt enum-head-nameopt enum-baseopt
enum-head-name:
	nested-name-specifieropt identifier
opaque-enum-declaration:
	enum-key attribute-specifier-seqopt nested-name-specifieropt identifier enum-baseopt ;
enum-key:
	enum
	enum class
	enum struct
enum-base:
	: type-specifier-seq
enumerator-list:
	enumerator-definition
	enumerator-list , enumerator-definition
enumerator-definition:
	enumerator
	enumerator = constant-expression
enumerator:
	identifier attribute-specifier-seqopt

Необязательное attribute-specifier-seqв enum-headи opaque-enum-declarationпринадлежит перечислению; атрибуты в нем attribute-specifier-seqвпоследствии считаются атрибутами перечисления, когда бы оно ни было названо. : Следующий « » в пределах от обрабатываются как часть . [ Это устраняет потенциальную двусмысленность между объявлением перечисления с помощью и объявлением безымянного битового поля перечислимого типа. [enum nested-name-specifieropt identifierdecl-specifier-seqmember-declarationenum-baseNote: enum-baseExample:

   struct S {
     enum E : int {};
     enum E : int {};           // error: redeclaration of enumeration
   };

end example] ] Если содержит , то декларация должна быть .end noteopaque-enum-declarationnested-name-specifier explicit specialization

Тип перечисления, объявленный с помощью enum-key of only, enum - это an unscoped enumeration, а его enumerators are unscoped enumerators. Операторы enum-keys enum class и enum struct семантически эквивалентны; тип перечисления объявляются с одним из них является scoped enumeration, и ее enumerators являются scoped enumerators. Необязательный параметр identifierне может быть опущен в объявлении перечисления с ограниченным объемом. Объект type-specifier-seqan enum-base должен называть целочисленный тип; любая CV-квалификация игнорируется. Объявление opaque-enum-declarationперечисления без области действия не должно пропускать enum-base. Идентификаторы в объекте enumerator-listобъявлены как константы и могут появляться везде, где требуются константы. Символ enumerator-definitionс = дает связанное enumeratorзначение, указанное с помощью constant-expression. Если у первого enumerator нет initializer, значение соответствующей константы равно нулю. Знак enumerator-definitionбез знака initializerдает enumeratorзначение, полученное путем увеличения значения предыдущего enumerator на единицу. [Example:

enum { a, b, c=0 };
enum { d, e, f=e+2 };

определяет a, cи d быть нулем, b и e быть 1, и f быть 3. ] Необязательный в входит в этот счетчику.end exampleattribute-specifier-seqenumerator

An opaque-enum-declarationявляется либо повторным объявлением перечисления в текущей области, либо объявлением нового перечисления. [ Note: Перечисление, объявленное объектом, opaque-enum-declarationимеет фиксированный базовый тип и является полным типом. Список счетчиков может быть предоставлен позже при повторном объявлении с расширением enum-specifier. ] Перечисление с ограниченной областью действия не должно позже повторно объявляться как не имеющее области действия или с другим базовым типом. Перечисление с незаданной областью не должно позже повторно объявляться как ограниченное, и каждое повторное объявление должно включать указание того же базового типа, что и в исходном объявлении. end noteenum-base

Если за enum-keyсимволом следует a nested-name-specifier, то enum-specifierдолжен ссылаться на перечисление, которое ранее было объявлено непосредственно в классе или пространстве имен, на которое nested-name-specifierссылается (т. Е. Не унаследовано и не введено a using-declaration), и enum-specifierдолжно появиться в пространстве имен, включающем предыдущее объявление.

Каждое перечисление определяет тип, отличный от всех других типов. Каждое перечисление также имеет underlying type. Базовый тип можно явно указать с помощью enum-base. Для типа перечисления с ограниченной областью действия базовый тип - int это если он не указан явно. В обоих случаях говорят, что основным типом является fixed. После закрывающей фигурной скобки у enum-specifierкаждого перечислителя указан тип его перечисления. Если базовый тип фиксирован, тип каждого перечислителя перед закрывающей фигурной скобкой является базовым типом, а constant-expressionв enumerator-definition должен быть converted constant expression базовым типом. Если базовый тип не фиксирован, тип каждого перечислителя перед закрывающей фигурной скобкой определяется следующим образом:

  • Если для перечислителя указан инициализатор, то constant-expressionдолжен быть integral constant expression. Если выражение имеет тип перечисления с незаданной областью, перечислитель имеет базовый тип этого типа перечисления, в противном случае он имеет тот же тип, что и выражение.

  • Если для первого перечислителя не указан инициализатор, его тип является неопределенным целочисленным типом со знаком.

  • В противном случае тип перечислителя такой же, как и у предыдущего перечислителя, если только увеличиваемое значение не может быть представлено в этом типе, и в этом случае тип является неопределенным интегральным типом, достаточным для содержания увеличенного значения. Если такого типа не существует, программа плохо сформирована.

Перечисление, базовый тип которого фиксирован, является неполным типом от его point of declaration до сразу после него enum-base(если есть), и в этот момент оно становится полным типом. Перечисление базовый тип которого не фиксируется неполный тип с его точки к декларации сразу же после закрытия } из ее enum-specifier, и в этот момент он становится полным типа.

Для перечисления, базовый тип которого не фиксирован, базовый тип является интегральным типом, который может представлять все значения перечислителя, определенные в перечислении. Если ни один целочисленный тип не может представить все значения перечислителя, это означает, что перечисление имеет неправильный формат. Это определяется реализацией, какой интегральный тип используется в качестве базового типа, за исключением того, что базовый тип не должен быть больше, чем int если значение перечислителя не может поместиться в int или unsigned int. Если enumerator-listпусто, базовый тип выглядит так, как если бы в перечислении был один перечислитель со значением 0.

Для перечисления, базовый тип которого фиксирован, значения перечисления являются значениями базового типа. В противном случае, для перечисления, где emin наименьший перечислитель и emax наибольший, значения перечисления - это значения в диапазоне bmin до bmax, определяемом следующим образом: Пусть K будет 1 для представления с дополнением до двух и 0 для дополнения до единиц или знака - представление величины. bmax - наименьшее значение, большее или равное max(|emin|K,|emax|) и равное 2M1, где M - неотрицательное целое число. bmin равно нулю, если emin неотрицательно, и в (bmax+K) противном случае. Размер самого маленького битового поля достаточно большой , чтобы вместить все значения типа перечисление , max(M,1) если bmin равен нулю , а в M+1 противном случае. Можно определить перечисление, значения которого не определены ни одним из его перечислителей. Если enumerator-listпусто, значения перечисления такие, как если бы перечисление имело единственный перечислитель со значением 0.96

Два типа перечисления - это layout-compatible enumerations если они имеют один и тот же базовый тип.

Значение перечислителя или объекта перечислимого типа с незаданной областью преобразуется в целое число с помощью integral promotion. [Example:

  enum color { red, yellow, green=20, blue };
  color col = red;
  color* cp = &col;
  if (*cp == blue)              // ...

создает color тип, описывающий различные цвета, а затем объявляет col как объект этого типа и cp как указатель на объект этого типа. Возможные значения объекта типа color являются red, yellow, green, blue; эти значения могут быть преобразованы в целые значения 0, 1, 20и 21. Поскольку перечисления относятся к разным типам, объектам типа color могут быть присвоены только значения типа color.

color c = 1;                    // error: type mismatch, no conversion from int to color
int i = yellow;                 // OK: yellow converted to integral value 1, integral promotion

Обратите внимание , что это неявный enum для int преобразования не предусмотрен для контекстного перечисления:

enum class Col { red, yellow, green };
int x = Col::red;               // error: no Col to int conversion
Col y = Col::red;
if (y) { }                      // error: no Col to bool conversion

end example]

Все без enum-nameисключения объекты с незаданной областью enumeratorобъявляются в области, которая непосредственно содержит enum-specifier. Каждая область видимости enumeratorобъявляется в области действия перечисления. Эти имена подчиняются правилам области видимости, определенным для всех имен в [basic.scope] и [basic.lookup]. [Example:

enum direction { left='l', right='r' };

void g()  {
  direction d;                  // OK
  d = left;                     // OK
  d = direction::right;         // OK
}

enum class altitude { high='h', low='l' };

void h()  {
  altitude a;                   // OK
  a = high;                     // error: high not in scope
  a = altitude::low;            // OK
}

end example] На перечислитель, объявленный в области класса, можно ссылаться с помощью операторов доступа к членам класса (​::​, . (точка) и -> (стрелка)), см [expr.ref]. [Example:

struct X {
  enum direction { left='l', right='r' };
  int f(int i) { return i==left ? 0 : i==right ? 1 : 2; }
};

void g(X* p) {
  direction d;                  // error: direction not in scope
  int i;
  i = p->f(left);               // error: left not in scope
  i = p->f(X::right);           // OK
  i = p->f(p->left);            // OK
  // ...
}

end example]

Если an enum-headсодержит a nested-name-specifier, то enum-specifierдолжен ссылаться на перечисление, которое ранее было объявлено непосредственно в классе или пространстве имен, на которое nested-name-specifierссылается, или в элементе встроенного пространства имен set ([namespace.def]) этого пространства имен (т. Е. Не просто унаследовано или введено using-declaration), и enum-specifierдолжен появиться в пространстве имен, включающем предыдущее объявление. В таких случаях nested-name-specifier из enum-headопределения не должно начинаться с decltype-specifier.

Этот набор значений используется для определения семантики продвижения и преобразования для типа перечисления. Это не препятствует тому, чтобы выражение типа перечисления могло иметь значение, выходящее за пределы этого диапазона.

10.3 Namespaces [basic.namespace]

Пространство имен - это декларативная область с дополнительным именем. Имя пространства имен может использоваться для доступа к объектам, объявленным в этом пространстве имен; то есть члены пространства имен. В отличие от других декларативных областей, определение пространства имен может быть разделено на несколько частей одной или нескольких единиц перевода.

Самая внешняя декларативная область единицы перевода - это пространство имен; см [basic.scope.namespace].

10.3.1 Namespace definition [namespace.def]

namespace-name:
	identifier
	namespace-alias
namespace-definition:
	named-namespace-definition
	unnamed-namespace-definition
	nested-namespace-definition
named-namespace-definition:
	inlineopt namespace attribute-specifier-seqopt identifier { namespace-body }
unnamed-namespace-definition:
	inlineopt namespace attribute-specifier-seqopt { namespace-body }
nested-namespace-definition:
	namespace enclosing-namespace-specifier :: identifier { namespace-body }
enclosing-namespace-specifier:
	identifier
	enclosing-namespace-specifier :: identifier
namespace-body:
	declaration-seqopt

Каждый namespace-definitionдолжен появиться в глобальной области видимости или в области видимости пространства имен ([basic.scope.namespace]).

В named-namespace-definition, то identifierэто имя пространства имен. Если identifierпри поиске ([basic.lookup.unqual]) относится к namespace-name(но не a namespace-alias), которое было введено в пространство имен, в котором named-namespace-definitionпоявляется, или которое было введено в члене встроенного набора пространств имен этого пространства имен, namespace-definition extends ранее объявленное пространство имен. В противном случае identifierвводится как namespace-nameв декларативную область, в которой named-namespace-definitionпоявляется.

Поскольку a namespace-definitionсодержится declarations в его, namespace-bodyа a namespace-definitionсамо является a declaration, отсюда следует, что он namespace-definitions может быть вложенным. [Example:

namespace Outer {
  int i;
  namespace Inner {
    void f() { i++; }           // Outer​::​i
    int i;
    void g() { i++; }           // Inner​::​i
  }
}

end example]

Объектами enclosing namespaces объявления являются те пространства имен, в которых объявление появляется лексически, за исключением повторного объявления члена пространства имен вне его исходного пространства имен (например, определения, как указано в [namespace.memdef]). Такое повторное объявление имеет те же охватывающие пространства имен, что и исходное объявление. [Example:

namespace Q {
  namespace V {
    void f();                   // enclosing namespaces are the global namespace, Q, and Q​::​V
    class C { void m(); };
  }
  void V::f() {                 // enclosing namespaces are the global namespace, Q, and Q​::​V
    extern void h();            // ... so this declares Q​::​V​::​h
  }
  void V::C::m() {              // enclosing namespaces are the global namespace, Q, and Q​::​V
  }
}

end example]

Если необязательное начальное inline ключевое слово появляется в a namespace-definitionдля определенного пространства имен, это пространство имен объявляется inline namespace. inline Ключевое слово может быть использовано на namespace-definitionкоторый расширяет пространство имен , только если он был ранее использован на namespace-definition том , что изначально объявлено namespace-nameдля этого пространства имен.

Необязательный параметр attribute-specifier-seq в a named-namespace-definition принадлежит определяемому или расширяемому пространству имен.

Члены встроенного пространства имен можно использовать во многих отношениях, как если бы они были членами включающего пространства имен. В частности, встроенное пространство имен и его включающее пространство имен добавляются к набору связанных пространств имен, используемых argument-dependent lookup всякий раз, когда одно из них есть, а элемент, using-directiveкоторый именует встроенное пространство имен, неявно вставляется во включающее пространство имен, как для unnamed namespace. Кроме того, каждый член инлайн пространства имен может впоследствии быть partially specialized, explicitly instantiatedили , explicitly specialized как если бы она была членом пространства имен вмещающих. Наконец, поиск имени в охватывающем пространстве имен с помощью явной qualification ([namespace.qual]) будет включать элементы встроенного пространства имен, внесенных с помощью, using-directiveдаже если есть объявления этого имени во включающем пространстве имен.

Эти свойства являются транзитивными: если пространство имен N содержит встроенное пространство имен M, которое, в свою очередь, содержит встроенное пространство имен O, тогда члены O могут использоваться, как если бы они были членами M или N. inline namespace set Из N является транзитивным замыканием всех встроенных пространств имен в N. enclosing namespace set Из O есть множество пространств имен , состоящие из самих внутренних без встроенного пространства имен , охватывающего инлайн пространства имен O, вместе с любыми промежуточным инлайн пространствами имен.

A nested-namespace-definitionс символом enclosing-namespace-specifier E, identifier I и namespace-body B эквивалентно

namespace E { namespace I { B } }

[Example:

namespace A::B::C {
  int i;
}

Вышеупомянутое имеет тот же эффект, что и:

namespace A {
  namespace B {
    namespace C {
      int i;
    }
  }
}

end example]

10.3.1.1 Unnamed namespaces [namespace.unnamed]

Ведет unnamed-namespace-definitionсебя так, как если бы его заменили на

inlineopt namespace unique { /* empty body */ }
using namespace unique ;
namespace unique { namespace-body }

где inline появляется тогда и только тогда, когда он появляется в unnamed-namespace-definition и все вхождения unique в единицу перевода заменяются одним и тем же идентификатором, и этот идентификатор отличается от всех других идентификаторов в единице перевода. Опциональная attribute-specifier-seq в unnamed-namespace-definition appertains к unique. [Example:

namespace { int i; }            // unique​::​i
void f() { i++; }               // unique​::​i++

namespace A {
  namespace {
    int i;                      // A​::​unique​::​i
    int j;                      // A​::​unique​::​j
  }
  void g() { i++; }             // A​::​unique​::​i++
}

using namespace A;
void h() {
  i++;                          // error: unique​::​i or A​::​unique​::​i
  A::i++;                       // A​::​unique​::​i
  j++;                          // A​::​unique​::​j
}

end example]

10.3.1.2 Namespace member definitions [namespace.memdef]

Объявление в пространстве имен N (за исключением объявлений во вложенных областях), declarator-idкоторое является unqualified-id([dcl.meaning]), class-head-nameили enum-head-nameявляется identifier, или elaborated-type-specifierимеет форму ( ), или это , объявляет (или повторно объявляет) его или в качестве члена . [ Или шаблон не ввести имя и , таким образом , может быть объявлено используя в качестве члена множества вшито пространства имен, если основной шаблон объявлен в инлайн пространстве имен. ] [class-key attribute-specifier-seqopt identifier[dcl.type.elab]opaque-enum-declarationunqualified-ididentifier NNote: explicit instantiation explicit specialization unqualified-idend noteExample:

namespace X {
  void f() { /* ... */ }        // OK: introduces X​::​f()

  namespace M {
    void g();                   // OK: introduces X​::​M​::​g()
  }
  using M::g;
  void g();                     // error: conflicts with X​::​M​::​g()
}

end example]

Члены именованного пространства имен также могут быть определены вне этого пространства имен с помощью явной qualification ([namespace.qual]) определяемого имени при условии, что определяемая сущность уже была объявлена ​​в пространстве имен и определение появляется после точки объявления в пространстве имен, которое включает пространство имен декларации. [Example:

namespace Q {
  namespace V {
    void f();
  }
  void V::f() { /* ... */ }     // OK
  void V::g() { /* ... */ }     // error: g() is not yet a member of V
  namespace V {
    void g();
  }
}

namespace R {
  void Q::V::g() { /* ... */ }  // error: R doesn't enclose Q
}

end example]

Если friend объявление в нелокальном классе сначала объявляет класс, функцию, шаблон класса или шаблон функции,97 то друг является членом самого внутреннего включающего пространства имен. friend Декларация сама по себе не делает имя видимым для неквалифицированного поиска ([basic.lookup.unqual]) или квалифицированного поиска ([basic.lookup.qual]). [ Note: Имя друга будет видно в его пространстве имен, если соответствующее объявление предоставлено в области пространства имен (либо до, либо после определения класса, предоставляющего дружбу). ] Если вызывается дружественная функция или шаблон функции, ее имя можно найти с помощью поиска по именам, который рассматривает функции из пространств имен и классов, связанных с типами аргументов функции ( ). Если имя в объявлении не является ни квалифицированным, ни a, а объявление является функцией или объектом, поиск для определения того, была ли объект объявлен ранее, не должен учитывать какие-либо области за пределами самого внутреннего включающего пространства имен. [ Другие формы объявлений не могут объявлять новый член самого внутреннего включающего пространства имен и, таким образом, следовать обычным правилам поиска. ] [end note[basic.lookup.argdep] friend template-idelaborated-type-specifierNote: friend end noteExample:

// Assume f and g have not yet been declared.
void h(int);
template <class T> void f2(T);
namespace A {
  class X {
    friend void f(X);           // A​::​f(X) is a friend
    class Y {
      friend void g();          // A​::​g is a friend
      friend void h(int);       // A​::​h is a friend
                                // ​::​h not considered
      friend void f2<>(int);    // ​::​f2<>(int) is a friend
    };
  };

  // A​::​f, A​::​g and A​::​h are not visible here
  X x;
  void g() { f(x); }            // definition of A​::​g
  void f(X) { /* ... */ }       // definition of A​::​f
  void h(int) { /* ... */ }     // definition of A​::​h
  // A​::​f, A​::​g and A​::​h are visible here and known to be friends
}

using A::x;

void h() {
  A::f(x);
  A::X::f(x);                   // error: f is not a member of A​::​X
  A::X::Y::g();                 // error: g is not a member of A​::​X​::​Y
}

end example]

это означает, что имя класса или функции неквалифицировано.

10.3.2 Namespace alias [namespace.alias]

A namespace-alias-definitionобъявляет альтернативное имя для пространства имен в соответствии со следующей грамматикой:

namespace-alias:
	identifier
namespace-alias-definition:
	namespace identifier = qualified-namespace-specifier ;
qualified-namespace-specifier:
	nested-name-specifieropt namespace-name

Символ identifierв a namespace-alias-definitionявляется синонимом имени пространства имен, обозначаемого символом qualified-namespace-specifierи становится a namespace-alias. [ Note: При поиске a namespace-nameв a namespace-alias-definitionучитываются только имена пространств имен, см [basic.lookup.udir]. ]end note

В декларативной области a namespace-alias-definitionможно использовать для переопределения, namespace-aliasобъявленного в этой декларативной области, чтобы ссылаться только на пространство имен, на которое оно уже ссылается. [ Example: Следующие декларации составлены правильно:

namespace Company_with_very_long_name { /* ... */ }
namespace CWVLN = Company_with_very_long_name;
namespace CWVLN = Company_with_very_long_name;  // OK: duplicate
namespace CWVLN = CWVLN;

end example]

10.3.3 The using declaration [namespace.udecl]

using-declaration:
	using using-declarator-list ;
using-declarator-list:
	using-declarator ...opt
	using-declarator-list , using-declarator ...opt
using-declarator:
	typenameopt nested-name-specifier unqualified-id

Каждый using-declaratorв a using-declaration98 вводит набор объявлений в декларативную область, в которой using-declarationпоявляется. Набор объявлений, представленных объектом, using-declaratorнаходится путем выполнения поиска по квалифицированному имени ([basic.lookup.qual], [class.member.lookup]) для имени в using-declarator, за исключением функций, которые скрыты, как описано ниже. Если using-declaratorне называет конструктор, unqualified-idобъявляется в декларативной области, в которой using-declarationпоявляется как синоним для каждого объявления, представленного using-declarator. [ Note: Так объявляется только указанное имя; указание имени перечисления в a using-declaration не объявляет его перечислители в using-declarationдекларативной области. ] Если именует конструктор, он объявляет, что класс - это набор объявлений конструктора, представленных от назначенного базового класса.end noteusing-declarator inherits using-declarator

Every using-declaration- это a declarationи a, member-declarationпоэтому их можно использовать в определении класса. [Example:

struct B {
  void f(char);
  void g(char);
  enum E { e };
  union { int x; };
};

struct D : B {
  using B::f;
  void f(int) { f('c'); }       // calls B​::​f(char)
  void g(int) { g('c'); }       // recursively calls D​::​g(int)
};

end example]

В using-declarationиспользуются в качестве member-declaration, каждый using-declarator«s nested-name-specifier будет называть базовый класс определяемым класса. Если a using-declaratorназывает конструктор, он nested-name-specifierдолжен называть прямой базовый класс определяемого класса. [Example:

template <typename... bases>
struct X : bases... {
  using bases::g...;
};

X<B, D> x;                      // OK: B​::​g and D​::​g introduced

end example] [Example:

class C {
  int g();
};

class D2 : public B {
  using B::f;                   // OK: B is a base of D2
  using B::e;                   // OK: e is an enumerator of base B
  using B::x;                   // OK: x is a union member of base B
  using C::g;                   // error: C isn't a base of D2
};

end example]

[ Note: Поскольку деструкторы не имеют имен, a using-declarationне может ссылаться на деструктор базового класса. Поскольку специализации шаблонов членов для функций преобразования не могут быть найдены с помощью поиска по имени, они не учитываются, когда a using-declarationуказывает функцию преобразования ([temp.mem]). ] Если конструктор или оператор присваивания, перенесенный из базового класса в производный класс, имеет сигнатуру конструктора копирования / перемещения или оператора присваивания для производного класса ( ), сам по себе не подавляет неявное объявление члена производного класса; член базового класса скрыт или переопределен неявно объявленным конструктором копирования / перемещения или оператором присваивания производного класса, как описано ниже.end note[class.copy]using-declaration

A using-declarationне должен называть a template-id. [Example:

struct A {
  template <class T> void f(T);
  template <class T> struct X { };
};
struct B : A {
  using A::f<double>;           // ill-formed
  using A::X<int>;              // ill-formed
};

end example]

A using-declarationне должен называть пространство имен.

A using-declarationне должен называть перечислителя с ограниченной областью видимости.

Имя using-declarationчлена класса должно быть member-declaration. [Example:

struct X {
  int i;
  static int s;
};

void f() {
  using X::i;                   // error: X​::​i is a class member and this is not a member declaration.
  using X::s;                   // error: X​::​s is a class member and this is not a member declaration.
}

end example]

На члены, объявленные a, using-declarationможно ссылаться с помощью явной квалификации, как и на другие имена членов ([namespace.qual]). [Example:

void f();

namespace A {
  void g();
}

namespace X {
  using ::f;                    // global f
  using A::g;                   // A's g
}

void h()
{
  X::f();                       // calls ​::​f
  X::g();                       // calls A​::​g
}

end example]

A using-declaration- это a declarationи поэтому может использоваться повторно там, где (и только когда) разрешено несколько объявлений. [Example:

namespace A {
  int i;
}

namespace A1 {
  using A::i, A::i;             // OK: double declaration
}

struct B {
  int i;
};

struct X : B {
  using B::i, B::i;             // error: double member declaration
};

end example]

[ Note: Для элемента, using-declaration чьи nested-name-specifierимена являются пространством имен, члены, добавленные к пространству имен после элемента using-declaration , не входят в набор представленных объявлений, поэтому они не учитываются при использовании имени. Таким образом, дополнительные перегрузки, добавленные после using-declaration, игнорируются, но учитываются аргументы функции по умолчанию ([dcl.fct.default]), аргументы шаблона по умолчанию ([temp.param]) и специализации шаблона ([temp.class.spec], [temp.expl.spec]). ] [ end noteExample:

namespace A {
  void f(int);
}

using A::f;         // f is a synonym for A​::​f; that is, for A​::​f(int).
namespace A {
  void f(char);
}

void foo() {
  f('a');           // calls f(int), even though f(char) exists.
}

void bar() {
  using A::f;       // f is a synonym for A​::​f; that is, for A​::​f(int) and A​::​f(char).
  f('a');           // calls f(char)
}

end example]

[ Note: Частичные специализации шаблонов классов можно найти путем поиска основного шаблона класса и последующего рассмотрения всех частичных специализаций этого шаблона. Если a using-declarationназывает шаблон класса, частичные специализации, введенные после using-declaration, фактически видны, потому что первичный шаблон является видимым ([temp.class.spec]). ]end note

Поскольку a using-declarationявляется объявлением, ограничения на одноименное объявление в нем declarative region также применяются к using-declarations. [Example:

namespace A {
  int x;
}

namespace B {
  int i;
  struct g { };
  struct x { };
  void f(int);
  void f(double);
  void g(char);     // OK: hides struct g
}

void func() {
  int i;
  using B::i;       // error: i declared twice
  void f(char);
  using B::f;       // OK: each f is a function
  f(3.5);           // calls B​::​f(double)
  using B::g;
  g('a');           // calls B​::​g(char)
  struct g g1;      // g1 has class type B​::​g
  using B::x;
  using A::x;       // OK: hides struct B​::​x
  x = 99;           // assigns to A​::​x
  struct x x1;      // x1 has class type B​::​x
}

end example]

Если объявление функции в области пространства имен или области блока имеет то же имя и то же самое, parameter-type-list что и функция, представленная с помощью a using-declaration, и объявления не объявляют ту же функцию, программа имеет неправильный формат. Если объявление шаблона функции в области пространства имен имеет то же имя, список типов параметров, возвращаемый тип и список параметров шаблона, что и шаблон функции, введенный с помощью a using-declaration, программа имеет неправильный формат. [ Note: Двое using-declarations могут вводить функции с одним и тем же именем и одним и тем же списком типов параметров. Если для вызова неквалифицированного имени функции разрешение перегрузки функции выбирает функции, введенные таким образом using-declarations, вызов функции имеет неправильный формат. [Example:

namespace B {
  void f(int);
  void f(double);
}
namespace C {
  void f(int);
  void f(double);
  void f(char);
}

void h() {
  using B::f;       // B​::​f(int) and B​::​f(double)
  using C::f;       // C​::​f(int), C​::​f(double), and C​::​f(char)
  f('h');           // calls C​::​f(char)
  f(1);             // error: ambiguous: B​::​f(int) or C​::​f(int)?
  void f(int);      // error: f(int) conflicts with C​::​f(int) and B​::​f(int)
}

end example] ]end note

Когда a using-declaratorпереносит объявления из базового класса в производный класс, функции-члены и шаблоны функций-членов в производном классе переопределяют и / или скрывают функции-члены и шаблоны функций-членов с тем же именем parameter-type-list,, cv-qualification и ref-qualifier(если есть) в базовый класс (а не конфликтующий). Такие скрытые или переопределенные объявления исключаются из набора объявлений, введенных платформой using-declarator. [Example:

struct B {
  virtual void f(int);
  virtual void f(char);
  void g(int);
  void h(int);
};

struct D : B {
  using B::f;
  void f(int);      // OK: D​::​f(int) overrides B​::​f(int);

  using B::g;
  void g(char);     // OK

  using B::h;
  void h(int);      // OK: D​::​h(int) hides B​::​h(int)
};

void k(D* p)
{
  p->f(1);          // calls D​::​f(int)
  p->f('a');        // calls B​::​f(char)
  p->g(1);          // calls B​::​g(int)
  p->g('a');        // calls D​::​g(char)
}

struct B1 {
  B1(int);
};

struct B2 {
  B2(int);
};

struct D1 : B1, B2 {
  using B1::B1;
  using B2::B2;
};
D1 d1(0);           // ill-formed: ambiguous

struct D2 : B1, B2 {
  using B1::B1;
  using B2::B2;
  D2(int);          // OK: D2​::​D2(int) hides B1​::​B1(int) and B2​::​B2(int)
};
D2 d2(0);           // calls D2​::​D2(int)

end example]

В целях разрешения перегрузки функции, которые вводятся using-declarationв производный класс, обрабатываются так, как если бы они были членами производного класса. В частности, неявный this параметр следует рассматривать как указатель на производный класс, а не на базовый класс. Это не влияет на тип функции, и во всех других отношениях функция остается членом базового класса. Кроме того, конструкторы, которые вводятся через А using-declaration , рассматриваются как если бы они были конструкторами производного класса при поиске конструкторов производного класса ([class.qual]) или формирования набора кандидатов перегрузки ([over.match.ctor], [over.match.copy], [over.match.list]). Если такой конструктор выбран для выполнения инициализации объекта типа класса, все подобъекты, кроме базового класса, из которого произошел конструктор, инициализируются неявно ([class.inhctor.init]).

В a using-declarator, который не называет конструктор, все члены набора введенных объявлений должны быть доступны. В элементе, using-declaratorкоторый называет конструктор, проверка доступа не выполняется. В частности, если производный класс использует using-declaratorдля доступа к члену базового класса, имя члена должно быть доступно. Если имя совпадает с именем перегруженной функции-члена, тогда все указанные функции должны быть доступны. Члены базового класса, упомянутые a, using-declaratorдолжны быть видимы в области видимости по крайней мере одного из прямых базовых классов класса, в котором using-declaratorуказан.

[ Note: Поскольку a using-declaratorобозначает член базового класса (а не подобъект члена или функцию члена подобъекта базового класса), a using-declaratorнельзя использовать для разрешения унаследованных двусмысленностей членов. [Example:

struct A { int x(); };
struct B : A { };
struct C : A {
  using A::x;
  int x(int);
};

struct D : B, C {
  using C::x;
  int x(double);
};
int f(D* d) {
  return d->x();    // error: overload resolution selects A​::​x, but A is an ambiguous base class
}

end example] ]end note

Синоним, созданный a, using-declarationимеет обычную доступность для a member-declaration. Объект, using-declaratorкоторый называет конструктор, не создает синоним; вместо этого дополнительные конструкторы доступны, если они будут доступны при использовании для создания объекта соответствующего базового класса, а доступность using-declarationигнорируется. [Example:

class A {
private:
    void f(char);
public:
    void f(int);
protected:
    void g();
};

class B : public A {
  using A::f;       // error: A​::​f(char) is inaccessible
public:
  using A::g;       // B​::​g is a public synonym for A​::​g
};

end example]

Если a using-declaratorиспользует ключевое слово typename и указывает зависимое имя ([temp.dep]), имя, введенное с помощью using-declaration, обрабатывается как typedef-name.

A using-declarationс более чем одним using-declaratorэквивалентно соответствующей последовательности using-declarations с одним using-declaratorкаждым.

10.3.4 Using directive [namespace.udir]

using-directive:
	attribute-specifier-seqopt using  namespace nested-name-specifieropt namespace-name ;

A using-directiveне должен появляться в области класса, но может появляться в области пространства имен или в области блока. [ Note: При поиске a namespace-nameв a using-directiveучитываются только имена пространств имен, см [basic.lookup.udir]. ] Необязательный элемент принадлежит к .end noteattribute-specifier-sequsing-directive

A using-directiveуказывает, что имена в назначенном пространстве имен могут использоваться в области, в которой using-directiveпоявляется после using-directive. При unqualified name lookupэтом имена выглядят так, как если бы они были объявлены в ближайшем охватывающем пространстве имен, которое содержит как пространство имен, так using-directiveи назначенное пространство имен. [ Note: В этом контексте «содержит» означает «содержит прямо или косвенно». ]end note

A using-directiveне добавляет элементы в декларативную область, в которой находится. [Example:

namespace A {
  int i;
  namespace B {
    namespace C {
      int i;
    }
    using namespace A::B::C;
    void f1() {
      i = 5;        // OK, C​::​i visible in B and hides A​::​i
    }
  }
  namespace D {
    using namespace B;
    using namespace C;
    void f2() {
      i = 5;        // ambiguous, B​::​C​::​i or A​::​i?
    }
  }
  void f3() {
    i = 5;          // uses A​::​i
  }
}
void f4() {
  i = 5;            // ill-formed; neither i is visible
}

end example]

Для получения unqualified lookup, то using-directiveтранзитивность: если сфера содержит , using-directiveчто выдвигает второе пространство имен , которое само по себе содержит using-directives, эффект , как если бы using-directives из второго пространства имен также появилась в первом. [ Note: Для квалифицированного поиска см [namespace.qual]. ] [ end noteExample:

namespace M {
  int i;
}

namespace N {
  int i;
  using namespace M;
}

void f() {
  using namespace N;
  i = 7;            // error: both M​::​i and N​::​i are visible
}

Другой пример:

namespace A {
  int i;
}
namespace B {
  int i;
  int j;
  namespace C {
    namespace D {
      using namespace A;
      int j;
      int k;
      int a = i;    // B​::​i hides A​::​i
    }
    using namespace D;
    int k = 89;     // no problem yet
    int l = k;      // ambiguous: C​::​k or D​::​k
    int m = i;      // B​::​i hides A​::​i
    int n = j;      // D​::​j hides B​::​j
  }
}

end example]

Если пространство имен находится extended после a using-directiveдля этого пространства имен, дополнительные члены расширенного пространства имен и члены пространств имен, назначенные using-directives в расширении, namespace-definitionмогут использоваться после расширения namespace-definition.

Если поиск имени находит объявление для имени в двух разных пространствах имен, а объявления не объявляют одну и ту же сущность и не объявляют функции, использование имени неверно. [ Note: В частности, имя переменной, функции или перечислителя не скрывает имя класса или перечисления, объявленного в другом пространстве имен. Например,

namespace A {
  class X { };
  extern "C"   int g();
  extern "C++" int h();
}
namespace B {
  void X(int);
  extern "C"   int g();
  extern "C++" int h(int);
}
using namespace A;
using namespace B;

void f() {
  X(1);             // error: name X found in two namespaces
  g();              // OK: name g refers to the same entity
  h();              // OK: overload resolution selects A​::​h
}

end note]

Во время разрешения перегрузки все функции из транзитивного поиска рассматриваются для сопоставления аргументов. Набор объявлений, найденных при переходном поиске, неупорядочен. [ Note: В частности, порядок, в котором рассматривались пространства имен, и отношения между пространствами имен, подразумеваемые using-directives элементом, не приводят к тому, что предпочтение отдается никаким объявлениям, найденным в результате поиска. ] Двусмысленность существует, если наилучшее соответствие находит две функции с одинаковой сигнатурой, даже если одна находится в пространстве имен, доступном через пространство имен другой. [end noteusing-directives 99Example:

namespace D {
  int d1;
  void f(char);
}
using namespace D;

int d1;             // OK: no conflict with D​::​d1

namespace E {
  int e;
  void f(int);
}

namespace D {       // namespace extension
  int d2;
  using namespace E;
  void f(int);
}

void f() {
  d1++;             // error: ambiguous ​::​d1 or D​::​d1?
  ::d1++;           // OK
  D::d1++;          // OK
  d2++;             // OK: D​::​d2
  e++;              // OK: E​::​e
  f(1);             // error: ambiguous: D​::​f(int) or E​::​f(int)?
  f('a');           // OK: D​::​f(char)
}

end example]

Во время поиска имени в иерархии классов некоторые двусмысленности могут быть разрешены путем рассмотрения того, скрывает ли один член другого на некоторых путях ([class.member.lookup]). При рассмотрении набора имен, найденных в результате следования, такого разрешения неоднозначности нет using-directives.

10.4 The asm declaration [dcl.asm]

asm Декларация имеет вид

asm-definition:
	attribute-specifier-seqopt asm ( string-literal ) ;

asm Декларация условно-поддерживается; его значение определяется реализацией. Необязательный элемент attribute-specifier-seqв an asm-definitionотносится к asm объявлению. [ Note: Обычно он используется для передачи информации через реализацию ассемблеру. ]end note

10.6 Attributes [dcl.attr]

10.6.1 Attribute syntax and semantics [dcl.attr.grammar]

Атрибуты определяют дополнительную информацию для различных исходных конструкций, таких как типы, переменные, имена, блоки или единицы перевода.

attribute-specifier-seq:
	attribute-specifier-seqopt attribute-specifier
attribute-specifier:
	[ [ attribute-using-prefixopt attribute-list ] ]
	alignment-specifier
alignment-specifier:
	alignas ( type-id ...opt )
	alignas ( constant-expression ...opt )
attribute-using-prefix:
	using attribute-namespace :
attribute-list:
	attributeopt
	attribute-list , attributeopt
	attribute ...
	attribute-list , attribute ...
attribute:
	attribute-token attribute-argument-clauseopt
attribute-token:
	identifier
	attribute-scoped-token
attribute-scoped-token:
	attribute-namespace :: identifier
attribute-namespace:
	identifier
attribute-argument-clause:
	( balanced-token-seqopt )
balanced-token-seq:
	balanced-token
	balanced-token-seq balanced-token
balanced-token:
	( balanced-token-seqopt )
	[ balanced-token-seqopt ]
	{ balanced-token-seqopt }
	any token other than a parenthesis, a bracket, or a brace

Если an attribute-specifier содержит attribute-using-prefix, attribute-listследующее, которое attribute-using-prefix не должно содержать, attribute-scoped-token и каждый attribute-tokenв, который attribute-list обрабатывается так, как если бы его identifierпрефикс был N​::​, где N - это attribute-namespace указанное в attribute-using-prefix. [ Note: Это правило не накладывает ограничений на то, как объект attribute-using-prefix влияет на токены в attribute-argument-clause. ] [end noteExample:

[[using CC: opt(1), debug]]         // same as [[CC​::​opt(1), CC​::​debug]]
  void f() {}
[[using CC: opt(1)]] [[CC::debug]]  // same as [[CC​::​opt(1)]] [[CC​::​debug]]
  void g() {}
[[using CC: CC::opt(1)]]            // error: cannot combine using and scoped attribute token
  void h() {}

end example]

[ Note: Для каждого отдельного атрибута balanced-token-seqбудет указана форма . ] end note

В attribute-listобъекте многоточие может отображаться только в том случае, если это attributeразрешено спецификацией. attributeС последующим многоточием является pack expansion. Не , attribute-specifierчто не содержит attributes не имеет никакого эффекта. Порядок, в котором attribute-tokens появляются символы attribute-list, не имеет значения. Если a keyword или an, alternative token которое удовлетворяет синтаксическим требованиям identifier, содержится в an attribute-token, это считается идентификатором. Нет name lookup не выполняется ни с одним из идентификаторов, содержащихся в файле attribute-token. attribute-tokenОпределяет дополнительные требования к attribute-argument-clause(если таковые имеются).

Каждый из них attribute-specifier-seqотносится appertain к некоторому объекту или утверждению, идентифицированному синтаксическим контекстом, в котором он встречается (пункт [stmt.stmt], пункт [dcl.dcl], пункт [dcl.decl]). Если attribute-specifier-seqэлемент, принадлежащий какой-либо сущности или оператору, содержит элемент attributeили, alignment-specifierкоторый не может применяться к этой сущности или оператору, программа сформирована неправильно. Если a attribute-specifier-seq принадлежит a friend declaration, это объявление должно быть определением. Нет attribute-specifier-seqне относится к explicit instantiation.

Для attribute-token (включая attribute-scoped-token), не указанного в этом международном стандарте, поведение определяется реализацией. Все, attribute-tokenчто не распознается реализацией, игнорируется. [ Note: Каждая реализация должна выбрать отличительное имя для attribute-namespaceфайла attribute-scoped-token. ]end note

Два последовательные левые квадратные маркеры скобки должны появляться только тогда , когда внедрив attribute-specifierили в пределах balanced-token-seqот attribute-argument-clause. [ Note: Если две последовательные левые квадратные скобки появляются там, где attribute-specifierнедопустимо, программа имеет неправильный формат, даже если скобки соответствуют альтернативной грамматической постановке. ] [ end noteExample:

int p[10];
void f() {
  int x = 42, y[5];
  int(p[[x] { return x; }()]);  // error: invalid attribute on a nested declarator-id and
                                // not a function-style cast of an element of p.
  y[[] { return 2; }()] = 2;    // error even though attributes are not allowed in this context.
  int i [[vendor::attr([[]])]]; // well-formed implementation-defined attribute.
}

end example]

10.6.2 Alignment specifier [dcl.align]

alignment-specifier Может быть применена к переменной или к элементу данных класса, но оно не должно быть применено к битовое поле, параметр функции или exception-declaration([except.handle]). Также alignment-specifierможет применяться к объявлению или определению класса (в elaborated-type-specifierили class-head, соответственно) и к объявлению или определению перечисления (в opaque-enum-declarationили enum-head, соответственно ([dcl.enum])). alignment-specifierМноготочием является pack expansion.

Когда alignment-specifierимеет форму :alignas( constant-expression )

  • constant-expressionявляется неотъемлемой константным выражением

  • если постоянное выражение не оценивается как значение выравнивания ([basic.align]) или оценивается как расширенное выравнивание, а реализация не поддерживает это выравнивание в контексте объявления, программа имеет неправильный формат.

Элемент alignment-specifierформы имеет тот же эффект, что и ( ).alignas( type-id ) alignas(​alignof( type-id )) [expr.alignof]

Требование выравнивания объекта - это строжайшее ненулевое выравнивание, указанное в нем alignment-specifiers, если таковое имеется; в противном случае alignment-specifiers не действуют.

Комбинированный эффект all alignment-specifiers в объявлении не должен указывать менее строгое выравнивание, чем выравнивание, которое потребовалось бы для объявляемого объекта, если бы все, alignment-specifiers относящееся к этому объекту, было опущено. [Example:

struct alignas(8) S {};
struct alignas(1) U {
  S s;
};  // error: U specifies an alignment that is less strict than if the alignas(1) were omitted.

end example]

Если определяющее объявление объекта имеет alignment-specifier, любое не определяющее объявление этого объекта должно либо указывать эквивалентное выравнивание, либо не иметь alignment-specifier. И наоборот, если какое-либо объявление объекта имеет alignment-specifier, каждое определяющее объявление этого объекта должно указывать эквивалентное выравнивание. Диагностика не требуется, если объявления объекта имеют alignment-specifiers разные единицы перевода. [Example:

// Translation unit #1:
struct S { int x; } s, *p = &s;

// Translation unit #2:
struct alignas(16) S;           // error: definition of S lacks alignment, no diagnostic required
extern S* p;

end example]

[ Example: Выровненный буфер с требованием выравнивания A и содержащими N элементы типа T может быть объявлен как:

alignas(T) alignas(A) T buffer[N];

Указание alignas(T) гарантирует, что окончательное запрошенное выравнивание не будет слабее alignof(T), и, следовательно, программа не будет плохо сформирована. ]end example

[Example:

alignas(double) void f();                           // error: alignment applied to function
alignas(double) unsigned char c[sizeof(double)];    // array of characters, suitably aligned for a double
extern unsigned char c[sizeof(double)];             // no alignas necessary
alignas(float)
  extern unsigned char c[sizeof(double)];           // error: different alignment in declaration

end example]

10.6.3 Carries dependency attribute [dcl.attr.depend]

attribute-token carries_­dependency Распространения определяет зависимость в и из функций. Он должен появляться не более одного раза в каждом attribute-listи не attribute-argument-clauseдолжен присутствовать. Атрибут может быть применен к declarator-ida parameter-declarationв объявлении функции или лямбда-выражении, и в этом случае он указывает инициализацию параметра carries a dependency для каждого lvalue-to-rvalue conversion из этих объектов. Атрибут также может применяться к declarator-idобъявлению функции, и в этом случае он указывает, что возвращаемое значение, если оно есть, несет зависимость от оценки выражения вызова функции.

Первое объявление функции должно указывать carries_­dependency атрибут для ее, declarator-idесли какое-либо объявление функции указывает carries_­dependency атрибут. Кроме того, первое объявление функции должно указывать carries_­dependency атрибут для параметра, если какое-либо объявление этой функции указывает carries_­dependency атрибут для этого параметра. Если функция или один из ее параметров объявлен с carries_­dependency атрибутом в своем первом объявлении в одной единице трансляции, а та же функция или один из ее параметров объявлен без carries_­dependency атрибута в своем первом объявлении в другой единице трансляции, программа имеет неправильный формат. , диагностика не требуется.

[ Атрибут не изменяет значение программы, но может привести к образованию более эффективного кода. ]Note: carries_­dependency end note

[Example:

/* Translation unit A. */

struct foo { int* a; int* b; };
std::atomic<struct foo *> foo_head[10];
int foo_array[10][10];

[[carries_dependency]] struct foo* f(int i) {
  return foo_head[i].load(memory_order_consume);
}

int g(int* x, int* y [[carries_dependency]]) {
  return kill_dependency(foo_array[*x][*y]);
}

/* Translation unit B. */

[[carries_dependency]] struct foo* f(int i);
int g(int* x, int* y [[carries_dependency]]);

int c = 3;

void h(int i) {
  struct foo* p;

  p = f(i);
  do_something_with(g(&c, p->a));
  do_something_with(g(p->a, &c));
}

carries_­dependency Атрибут функция f означает , что возвращаемое значение несет в себе зависимость из f, так что необходимость осуществления не ограничивает порядок после возвращения из f. Реализации f и вызывающая сторона могут выбрать сохранение зависимостей вместо выдачи инструкций по упорядочиванию аппаратной памяти (также известных как заборы).

Функция gвторой параметр «s имеет carries_­dependency атрибут, но его первый параметр не делает. Следовательно, hпервый вызов функции g несет зависимость g, а ее второй вызов - нет. Реализации может потребоваться вставить забор перед вторым вызовом g.

end example]

10.6.4 Deprecated attribute [dcl.attr.deprecated]

attribute-token deprecated Могут быть использованы для имен меток и юридических лиц, использование которых по- прежнему допускается, но не рекомендуется для какой - то причине. [ Note: В частности, deprecated подходит для имен и организаций, которые считаются устаревшими или небезопасными. ] Он должен появляться не более одного раза в каждом . Может присутствовать и, если присутствует, то она должна иметь вид: end noteattribute-listattribute-argument-clause

( string-literal )

[ В может быть использовано для объяснения обоснования устаревания и / или предложить заменяющий объект. ]Note: string-literalattribute-argument-clauseend note

Атрибут может применяться к объявлению класса typedef-name, переменной, нестатического элемента данных, функции, пространства имен, перечисления, перечислителя или специализации шаблона.

Имя или сущность, объявленные без deprecated атрибута, позже могут быть повторно объявлены с помощью атрибута и наоборот. [ Note: Таким образом, объект, изначально объявленный без атрибута, может быть помечен как устаревший при последующем повторном объявлении. Однако после того, как объект помечен как устаревший, последующие повторные объявления не отменяют его. ] Допускаются повторные объявления с использованием разных форм атрибута (с или без или с разными ). end noteattribute-argument-clauseattribute-argument-clauses

[ Note: Реализации могут использовать deprecated атрибут для создания диагностического сообщения в случае, если программа обращается к имени или объекту, отличному от его объявления, после объявления, которое определяет атрибут. Диагностическое сообщение может включать в себя текст attribute-argument-clauseлюбого deprecated атрибута, применяемого к имени или объекту. ] end note

10.6.5 Fallthrough attribute [dcl.attr.fallthrough]

attribute-token fallthrough Может быть применен к null statement; такое заявление - провальное заявление. Они attribute-token fallthrough должны появляться не более одного раза в каждом attribute-listи не attribute-argument-clauseдолжны присутствовать. Заявление о провале может появиться только внутри вложенного switch statement. Следующим оператором, который будет выполняться после оператора перехода, должен быть помеченный оператор, метка которого является меткой case или меткой по умолчанию для того же switch оператора. Программа плохо сформирована, если такого оператора нет.

[ Note: Использование оператора fallthrough предназначено для подавления предупреждения, которое реализация могла бы в противном случае выдать для случая или метки по умолчанию, доступной из другого случая или метки по умолчанию на некотором пути выполнения. Реализациям рекомендуется выдавать предупреждение, если оператор сбоя недоступен динамически. ]end note

[Example:

void f(int n) {
  void g(), h(), i();
  switch (n) {
  case 1:
  case 2:
    g();
    [[fallthrough]];
  case 3:                       // warning on fallthrough discouraged
    h();
  case 4:                       // implementation may warn on fallthrough
    i();
    [[fallthrough]];            // ill-formed
  }
}

end example]

10.6.6 Maybe unused attribute [dcl.attr.unused]

Значок attribute-token maybe_­unused указывает на то, что имя или объект, возможно, намеренно не используются. Он должен появляться не более одного раза в каждом attribute-listи не attribute-argument-clauseдолжен присутствовать.

Атрибут может применяться к объявлению класса typedef-name, переменной, нестатического элемента данных, функции, перечисления или перечислителя.

[ Note: Для отмеченного объекта maybe_­unusedрекомендуется, чтобы реализации не выдавали предупреждения о том, что объект не используется или что объект используется, несмотря на наличие атрибута. ]end note

Имя или сущность, объявленные без maybe_­unused атрибута, позже могут быть повторно объявлены с атрибутом и наоборот. Сущность считается отмеченной после первого объявления, которое ее отмечает.

[Example:

[[maybe_unused]] void f([[maybe_unused]] bool thing1,
                        [[maybe_unused]] bool thing2) {
  [[maybe_unused]] bool b = thing1 && thing2;
  assert(b);
}

Реализациям рекомендуется не предупреждать о том, что b не используется, независимо от того, определено оно или нет NDEBUG . ]end example

10.6.7 Nodiscard attribute [dcl.attr.nodiscard]

attribute-token nodiscard Может быть применен к declarator-id в объявлении функции или к объявлению класса или перечисления. Он должен появляться не более одного раза в каждом attribute-listи не attribute-argument-clauseдолжен присутствовать.

[ Note: Вызов nodiscard - это выражение вызова функции, которое вызывает ранее объявленную функцию nodiscardили возвращаемый тип которой, возможно, является классом с квалификацией cv или отмеченным типом перечисления nodiscard. Появление вызова nodiscard как потенциально оцененного выражения отбрасываемого значения (пункт [expr]) не приветствуется, если явно не приведено к void. Реализациям рекомендуется выдавать предупреждение в таких случаях. Обычно это происходит из-за того, что отказ от возвращаемого значения вызова nodiscard имеет неожиданные последствия. ]end note

[Example:

struct [[nodiscard]] error_info { /* ... */ };
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
  enable_missile_safety_mode(); // warning encouraged
  launch_missiles();
}
error_info &foo();
void f() { foo(); }             // warning not encouraged: not a nodiscard call, because neither
                                // the (reference) return type nor the function is declared nodiscard

end example]

10.6.8 Noreturn attribute [dcl.attr.noreturn]

В attribute-token noreturn указывает , что функция не возвращает. Он должен появляться не более одного раза в каждом attribute-listи не attribute-argument-clauseдолжен присутствовать. Атрибут может быть применен к declarator-idв объявлении функции. Первое объявление функции должно указывать noreturn атрибут, если какое-либо объявление этой функции указывает noreturn атрибут. Если функция объявляется с noreturn атрибутом в одной единице трансляции, а та же функция объявляется без noreturn атрибута в другой единице трансляции, программа имеет неправильный формат и диагностика не требуется.

Если функция f вызывается там, где f ранее была объявлена ​​с noreturn атрибутом, и в f конечном итоге возвращается, поведение не определено. [ Note: Функция может завершиться выдачей исключения. ] [ Реализациям рекомендуется выдавать предупреждение, если отмеченная функция может вернуться. ] end noteNote: [[noreturn]] end note

[Example:

[[ noreturn ]] void f() {
  throw "error";                // OK
}

[[ noreturn ]] void q(int i) {  // behavior is undefined if called with an argument <= 0
  if (i > 0)
    throw "positive";
}

end example]