11 Declarators [dcl.decl]

Декларатор объявляет одну переменную, функцию или тип в объявлении. init-declarator-list Появляться в декларации разделенных запятыми последовательность declarators, каждый из которых может иметь инициализатор.

init-declarator-list:
	init-declarator
	init-declarator-list , init-declarator
init-declarator:
	declarator initializeropt

Три компонента a simple-declaration - это attributesспецификаторы ( decl-specifier-seq; [dcl.spec]) и деклараторы ( init-declarator-list). Спецификаторы указывают тип, класс хранения или другие свойства объявляемых сущностей. Деклараторы указывают имена этих сущностей и (необязательно) изменяют тип спецификаторов с помощью таких операторов, как * (указатель на) и () (возврат функции). Начальные значения также можно указать в деклараторе; инициализаторы обсуждаются в [dcl.init] и [class.init].

Каждое init-declarator в объявлении анализируется отдельно, как если бы оно было само по себе. [ Note: Объявление с несколькими деклараторами обычно эквивалентно соответствующей последовательности объявлений, каждое с одним декларатором. То есть

T D1, D2, ... Dn;

обычно эквивалентен

T D1; T D2; ... T Dn;

где T - а, decl-specifier-seq а каждый Di - ап init-declarator. Одно исключение - когда имя, введенное одним из элементов, declarators скрывает имя типа, используемое объектом decl-specifiers, так что, когда оно decl-specifiers используется в последующем объявлении, они не имеют того же значения, что и в

struct S { ... };
S S, T;                 // declare two instances of struct S

что не эквивалентно

struct S { ... };
S S;
S T;                    // error

Еще одно исключение, когда T это auto, например:

auto i = 1, j = 2.0;    // error: deduced types for i and j do not match

в отличие от

auto i = 1;             // OK: i deduced to have type int
auto j = 2.0;           // OK: j deduced to have type double

end note]

11.1 Type names [dcl.name]

Чтобы задать преобразования типов в явном виде, и в качестве аргумента sizeof, alignof, newили typeid, должно быть указано имя типа. Это можно сделать с помощью a type-id, который синтаксически является объявлением переменной или функции этого типа, в которой отсутствует имя объекта.

type-id:
	type-specifier-seq abstract-declaratoropt
defining-type-id:
	defining-type-specifier-seq abstract-declaratoropt
abstract-declarator:
	ptr-abstract-declarator
	noptr-abstract-declaratoropt parameters-and-qualifiers trailing-return-type
	abstract-pack-declarator
ptr-abstract-declarator:
	noptr-abstract-declarator
	ptr-operator ptr-abstract-declaratoropt
noptr-abstract-declarator:
	noptr-abstract-declaratoropt parameters-and-qualifiers
	noptr-abstract-declaratoropt [ constant-expressionopt ] attribute-specifier-seqopt
	( ptr-abstract-declarator )
abstract-pack-declarator:
	noptr-abstract-pack-declarator
	ptr-operator abstract-pack-declarator
noptr-abstract-pack-declarator:
	noptr-abstract-pack-declarator parameters-and-qualifiers
	noptr-abstract-pack-declarator [ constant-expressionopt ] attribute-specifier-seqopt
	...

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

int                 // int i
int *               // int *pi
int *[3]            // int *p[3]
int (*)[3]          // int (*p3i)[3]
int *()             // int *f()
int (*)(double)     // int (*pf)(double)

назовите соответственно типы «int», «указатель на int», «массив из 3 указателей на int», «указатель на массив из 3 int», «функция (без параметров), возвращающая указатель на int» и «указатель на функцию (double), возвращающую int». ]end example

Тип также может быть назван (часто более легко) с помощью typedef ([dcl.typedef]).

11.2 Ambiguity resolution [dcl.ambig.res]

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

struct S {
  S(int);
};

void foo(double a) {
  S w(int(a));                  // function declaration
  S x(int());                   // function declaration
  S y((int(a)));                // object declaration
  S y((int)a);                  // object declaration
  S z = int(a);                 // object declaration
}

end example]

Неоднозначность может возникнуть из-за сходства между приведением в стиле функции и типом type-id. Решение состоит в том, что любая конструкция, которая могла бы быть a type-id в своем синтаксическом контексте, должна считаться a type-id. [Example:

template <class T> struct X {};
template <int N> struct Y {};
X<int()> a;                     // type-id
X<int(1)> b;                    // expression (ill-formed)
Y<int()> c;                     // type-id (ill-formed)
Y<int(1)> d;                    // expression

void foo(signed char a) {
  sizeof(int());                // type-id (ill-formed)
  sizeof(int(a));               // expression
  sizeof(int(unsigned(a)));     // type-id (ill-formed)

  (int())+1;                    // type-id (ill-formed)
  (int(a))+1;                   // expression
  (int(unsigned(a)))+1;         // type-id (ill-formed)
}

end example]

Другая двусмысленность возникает в случае, parameter-declaration-clauseкогда a заключено type-name в круглые скобки. В этом случае выбор находится между объявлением параметра типа указателя на функцию и объявлением параметра с избыточными круглыми скобками вокруг declarator-id. Решение состоит в том, чтобы рассматривать type-name как a, simple-type-specifier а не как a declarator-id. [Example:

class C { };
void f(int(C)) { }              // void f(int(*fp)(C c)) { }
                                // not: void f(int C) { }

int g(C);

void foo() {
  f(1);                         // error: cannot convert 1 to function pointer
  f(g);                         // OK
}

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

class C { };
void h(int *(C[10]));           // void h(int *(*_­fp)(C _­parm[10]));
                                // not: void h(int *C[10]);

end example]

11.3 Meaning of declarators [dcl.meaning]

Декларатор содержит ровно один declarator-id; он называет объявленный идентификатор. unqualified-id Происходит в declarator-id должен быть простым identifier для заявления некоторых специальных функций , за исключением ([class.ctor], [class.conv], [class.dtor], [over.oper]) и для объявления специализаций шаблона или частичных специализаций ([temp.spec]). Когда declarator-id квалифицируется, объявление должно ссылаться на ранее объявленный член класса или пространства имен, к которому относится квалификатор (или, в случае пространства имен, элемента встроенного пространства имен этого пространства имен ([namespace.def])), или на его специализация; член должен не только был введен using-declaration в области видимости класса или пространства имен , выдвинутый nested-name-specifier из declarator-id. Аттестат nested-name-specifierквалифицированного declarator-idне должен начинаться с а decltype-specifier. [ Note: Если квалификатор является​::​ оператором разрешения глобальной области, то declarator-id ссылка на имя, объявленное в области глобального пространства имен. ] Необязательный параметр, следующий за a, относится к объявленной сущности.end noteattribute-specifier-seqdeclarator-id

static, thread_­local, extern, mutable, friend, inline, virtual, constexpr, explicit, Или typedef спецификатор применяется непосредственно к каждому declarator-id в init-declarator-listили member-declarator-list; тип, указанный для каждого, declarator-idзависит как от типа, так decl-specifier-seqи от его declarator.

Таким образом, объявление того или иного идентификатора имеет вид

T D

где T имеет форму и является декларатором. Ниже приводится рекурсивная процедура для определения типа, указанного для содержащегося в таком объявлении.attribute-specifier-seqopt decl-specifier-seqDdeclarator-id

Во-первых, decl-specifier-seq определяет тип. В декларации

T D

decl-specifier-seq T определяет тип T. [ Example: В декларации

int unsigned i;

спецификаторы типа int unsigned определяют тип «unsigned int» ([dcl.type.simple]). ]end example

В объявлении, attribute-specifier-seqopt T D где D - неукрашенный идентификатор, тип этого идентификатора - «T».

В декларации, T D где D есть форма

( D1 )

тип содержимого declarator-id такой же, как и тип содержащегося declarator-id в объявлении

T D1

Круглые скобки не изменяют тип встроенного declarator-id, но они могут изменить привязку сложных деклараторов.

11.3.1 Pointers [dcl.ptr]

В декларации, T D где D есть форма

* attribute-specifier-seqopt cv-qualifier-seqopt D1

и тип идентификатора в объявлении T D1 - «derived-declarator-type-list T», тогда тип идентификатора D - « указатель на »derived-declarator-type-list cv-qualifier-seqT . Применяется к указателю , а не объект указал. Точно так же необязательный элемент принадлежит указателю, а не указанному объекту.cv-qualifiersattribute-specifier-seq

[ Example: Заявления

const int ci = 10, *pc = &ci, *const cpc = pc, **ppc;
int i, *p, *const cp = &i;

объявить ci, постоянное целое число; pc, указатель на постоянное целое число; cpc, постоянный указатель на постоянное целое число; ppc, указатель на указатель на постоянное целое число; i, целое число; p, указатель на целое число; и cpпостоянный указатель на целое число. Значение ci, cpcи cp не может быть изменено после инициализации. Значение pc может быть изменено, как и объект, на который указывает cp. Примеры некоторых правильных операций:

i = ci;
*cp = ci;
pc++;
pc = cpc;
pc = p;
ppc = &pc;

Примеры плохо сформированных операций:

ci = 1;             // error
ci++;               // error
*pc = 2;            // error
cp = &ci;           // error
cpc++;              // error
p = pc;             // error
ppc = &p;           // error

Каждый из них неприемлем, потому что он либо изменит значение объявленного объекта, const либо позволит позже изменить его с помощью указателя cv-unqualified, например:

*ppc = &ci;         // OK, but would make p point to ci because of previous error
*p = 5;             // clobber ci

end example]

См. Также [expr.ass] и [dcl.init].

[ Note: Формирование указателя на ссылочный тип неверно; см [dcl.ref]. Формирование типа указателя функции неверно, если тип функции имеет cv-qualifiers или ref-qualifier; см [dcl.fct]. Поскольку адрес a bit-field не может быть взят, указатель никогда не может указывать на битовое поле. ]end note

11.3.2 References [dcl.ref]

В объявлении, T D где D есть одна из форм

& attribute-specifier-seqopt D1
&& attribute-specifier-seqopt D1

и тип идентификатора в объявлении T D1 - «derived-declarator-type-list T», тогда тип идентификатора D - «derived-declarator-type-list ссылка на T». Необязательный параметр attribute-specifier-seqотносится к ссылочному типу. Ссылки с квалификатором cv имеют неправильный формат, за исключением случаев, когда cv-квалификаторы вводятся с использованием символа typedef-name([dcl.typedef], [temp.param]) или decltype-specifier, в этом случае, cv-квалификаторы игнорируются. [Example:

typedef int& A;
const A aref = 3;   // ill-formed; lvalue reference to non-const initialized with rvalue

Типом aref является «ссылка lvalue на int», а не «ссылка lvalue на const int». ] [ Ссылку можно рассматривать как имя объекта. ] Объявление, указывающее тип «ссылка на », имеет неправильный формат.end exampleNote: end note cv void

Ссылочный тип, объявленный с использованием & , называется lvalue reference, а ссылочный тип, объявленный с использованием && , называется rvalue reference. Ссылки Lvalue и ссылки rvalue - это разные типы. Если явно не указано иное, они семантически эквивалентны и обычно называются ссылками.

[Example:

void f(double& a) { a += 3.14; }
// ...
double d = 0;
f(d);

объявляется a ссылочным параметром, f поэтому вызов f(d) будет добавлен 3.14 к d.

int v[20];
// ...
int& g(int i) { return v[i]; }
// ...
g(3) = 7;

объявляет функцию g() для возврата ссылки на целое число, поэтому она g(3)=7 будет назначена 7 четвертому элементу массива v. Другой пример:

struct link {
  link* next;
};

link* first;

void h(link*& p) {  // p is a reference to pointer
  p->next = first;
  first = p;
  p = 0;
}

void k() {
   link* q = new link;
   h(q);
}

объявляет p ссылку на указатель, link поэтому h(q) оставит q нулевое значение. См. Также [dcl.init.ref]. ]end example

Не указано, требуется ли для ссылки storage ([basic.stc]).

Не должно быть ссылок на ссылки, массивов ссылок и указателей на ссылки. Объявление ссылки должно содержать initializer ([dcl.init.ref]), за исключением случаев, когда объявление содержит явный extern спецификатор ([dcl.stc]), является class member объявлением в определении класса или является объявлением параметра или возвращаемого типа ([dcl.fct]); см [basic.def]. Ссылка должна быть инициализирована для ссылки на действительный объект или функцию. [ В частности, пустая ссылка не может существовать в четко определенной программе, потому что единственный способ создать такую ​​ссылку - это привязать ее к «объекту», полученному косвенным путем через нулевой указатель, что вызывает неопределенное поведение. Как описано в , ссылку нельзя напрямую привязать к битовому полю. ]Note: [class.bit]end note

Если typedef-name([dcl.typedef], [temp.param]) или a decltype-specifierобозначает тип, TR который является ссылкой на тип T, попытка создать тип «ссылка lvalue на cv TR» создает тип «ссылка lvalue на T», в то время как попытка создать тип «ссылка rvalue на cv TR» создает тип TR. [ Note: Это правило известно как сворачивание ссылок. ] [ end noteExample:

int i;
typedef int& LRI;
typedef int&& RRI;

LRI& r1 = i;                    // r1 has the type int&
const LRI& r2 = i;              // r2 has the type int&
const LRI&& r3 = i;             // r3 has the type int&

RRI& r4 = i;                    // r4 has the type int&
RRI&& r5 = 5;                   // r5 has the type int&&

decltype(r2)& r6 = i;           // r6 has the type int&
decltype(r2)&& r7 = i;          // r7 has the type int&

end example]

[ Note: Формирование ссылки на тип функции неверно, если тип функции имеет cv-qualifiers или ref-qualifier; см [dcl.fct]. ]end note

11.3.3 Pointers to members [dcl.mptr]

В декларации, T D где D есть форма

nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt D1

и nested-name-specifier обозначает класс, а тип идентификатора в объявлении T D1 - «derived-declarator-type-list T», тогда тип идентификатора D - « указатель на член класса типа »derived-declarator-type-list cv-qualifier-seqnested-name-specifierT . Необязательный attribute-specifier-seqэлемент принадлежит указателю на член.

[Example:

struct X {
  void f(int);
  int a;
};
struct Y;

int X::* pmi = &X::a;
void (X::* pmf)(int) = &X::f;
double X::* pmd;
char Y::* pmc;

объявляет pmi, pmf, pmd и ,pmc чтобы быть указателем на член X типа int, указатель на член X типа void(int), указатель на член X типа double и указатель на член Y типа char соответственно. Объявление pmd правильно сформировано, даже если X не имеет членов типа double. Точно так же pmc правильно сформировано объявление, даже если Y это неполный тип. pmi и pmf может использоваться так:

X obj;
// ...
obj.*pmi = 7;       // assign 7 to an integer member of obj
(obj.*pmf)(7);      // call a function member of obj with the argument 7

end example]

Указатель на член не должен указывать на static member класс, член со ссылочным типом или «cv void».

[ Note: См. Также [expr.unary] и [expr.mptr.oper]. Тип «указатель на член» отличается от типа «указатель», то есть указатель на член объявляется только синтаксисом указателя на декларатор члена, а не синтаксисом декларатора указателя. В C ++ нет типа «ссылка на член». ]end note

11.3.4 Arrays [dcl.array]

В декларации, T D где D есть форма

D1 [ constant-expressionopt ] attribute-specifier-seqopt

и тип идентификатора в объявлении T D1 - «derived-declarator-type-list T», тогда тип идентификатора D является типом массива; если тип идентификатора D содержит , значит, программа сформирована неправильно. называется массивом ; этот тип не должен быть ссылочным типом , типом функции или типом абстрактного класса. Если присутствует, это должно быть преобразованное постоянное выражение типа, и его значение должно быть больше нуля. Постоянное выражение определяет из (количество элементов в) массива. Если значение константного выражения равно , массив имеет элементы, пронумерованные до , а тип идентификатора - « массив из ». Объект типа массив содержит непрерывно выделенный непустой набор подобъектов типа . За исключением случаев, указанных ниже, если постоянное выражение опущено, тип идентификатора - « массив с неизвестной границей », неполный тип объекта. Тип « массив » отличается от типа « массив с неизвестной границей », см . Любой тип формы « массив » настраивается на «массив » и аналогично для «массив неизвестной границы ». Необязательный элемент принадлежит массиву. [ auto type-specifierTelement type cv voidconstant-expression std​::​size_­t boundNN0N-1Dderived-declarator-type-list N TNTDderived-declarator-type-list Tderived-declarator-type-list N Tderived-declarator-type-list T [basic.types]cv-qualifier-seqN TN cv-qualifier-seq TTattribute-specifier-seqExample:

typedef int A[5], AA[2][3];
typedef const A CA;             // type is “array of 5 const inttypedef const AA CAA;           // type is “array of 2 array of 3 const int

end example] [ Note: «Массив » имеет тип с квалификацией cv; см . ]N cv-qualifier-seq T [basic.type.qualifier]end note

Массив может быть построен из одного из основных типов (кроме void), из указателя, из указателя на член, из класса, из типа перечисления или из другого массива.

Когда несколько «массивов» спецификаций являются смежными, создается многомерный массив; может быть опущено только первое из константных выражений, определяющих границы массивов. В дополнение к объявлениям, в которых разрешен неполный тип объекта, в некоторых случаях при объявлении параметра функции ([dcl.fct]) может быть опущена граница массива . Связь с массивом также может быть опущена, если за декларатором следует initializerили когда за декларатором для статического члена данных следует brace-or-equal-initializer([class.mem]). В обоих случаях граница вычисляется из числа исходных элементов (скажем, N) предоставленных ([dcl.init.aggr]), а тип идентификатора D - «массив N T». Кроме того, если есть предыдущее объявление объекта в той же области, в которой была указана привязка, пропущенная граница массива считается такой же, как в этом более раннем объявлении, и аналогично для определения статического члена данных объекта класс.

[Example:

float fa[17], *afp[17];

объявляет массив float чисел и массив указателей на float числа. Другой пример:

static int x3d[3][5][7];

объявляет статический трехмерный массив целых чисел с рангом 3×5×7. В полной мере, x3d это массив из трех элементов; каждый элемент представляет собой массив из пяти массивов; каждый из последних массивов представляет собой массив из семи целых чисел. Любое из выражений x3d, x3d[i], x3d[i][j], x3d[i][j][k] может разумно появиться в выражении. Наконец-то,

extern int x[10];
struct S {
  static int y[10];
};

int x[];                // OK: bound is 10
int S::y[];             // OK: bound is 10

void f() {
  extern int x[];
  int i = sizeof(x);    // error: incomplete object type
}

end example]

[ Note: Преобразования, влияющие на выражения типа массива, описаны в [conv.array]. Объекты типов массивов не могут быть изменены, см [basic.lval]. ]end note

[ Note: За исключением случаев, когда он был объявлен для класса ([over.sub]), оператор нижнего индекса [] интерпретируется таким же образом, что E1[E2] и *((E1)+(E2)) ([expr.sub]). Из-за правил преобразования, которые применяются к +, if E1 является массивом и E2 целым числом, то E1[E2] относится к E2-ому члену E1. Таким образом, несмотря на асимметричный вид, индексирование является коммутативной операцией. ]end note

[ Note: Для многомерных массивов соблюдается единое правило . Если E является n-мерным массивом ранга i×j××k, то, E появляющееся в выражении, которое подлежит array-to-pointer conversion , преобразуется в указатель на (n1)-мерный массив с рангом j××k. Если * оператор, явно или неявно в результате индексации, применяется к этому указателю, результатом является указанный на (n1)размерный массив, который сам немедленно преобразуется в указатель. [ Example: Учтите

int x[3][5];

Вот x это 3×5 массив целых чисел. Когда x появляется в выражении, оно преобразуется в указатель на (первый из трех) пятичленный массив целых чисел. В выражении, x[i] которое эквивалентно *(x+i), x сначала преобразуется в указатель, как описано; then x+i преобразуется в тип x, который включает умножение i на длину объекта, на который указывает указатель, а именно пяти целочисленных объектов. Результаты складываются, и косвенное обращение применяется для получения массива (из пяти целых чисел), который, в свою очередь, преобразуется в указатель на первое из целых чисел. Если есть другой нижний индекс, тот же аргумент применяется снова; на этот раз результат - целое число. ] ]end exampleend note

[ Note: Из всего этого следует, что массивы в C ++ хранятся построчно (последний индекс изменяется быстрее всего) и что первый индекс в объявлении помогает определить объем памяти, потребляемой массивом, но не играет никакой другой роли в вычислениях индекса. ]end note

11.3.5 Functions [dcl.fct]

В декларации, T D где D есть форма

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt

и тип, содержащийся declarator-id в объявлении, T D1 - «derived-declarator-type-list T», тип declarator-id in D - «derived-declarator-type-list noexceptopt функция возврата ( parameter-declaration-clause) », где необязательный элемент присутствует тогда и только тогда, когда он не выбрасывает. Необязательный параметр относится к типу функции.cv-qualifier-seqopt ref-qualifieropt T noexcept exception specification attribute-specifier-seq

В декларации, T D где D есть форма

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-type

и тип, содержащийся declarator-id в объявлении T D1 «derived-declarator-type-list T», T должен быть единственным type-specifier auto. Тип declarator-id in D - это «derived-declarator-type-list noexceptopt функция возврата ( parameter-declaration-clause) », где - тип, указанный в , и где необязательный параметр присутствует тогда и только тогда, когда спецификация исключения не выбрасывает. Необязательный параметр относится к типу функции.cv-qualifier-seqopt ref-qualifieropt U U trailing-return-type noexcept attribute-specifier-seq

parameter-declaration-clause Определяет аргументы , которые могут быть определены, и их обработка, когда функция вызывается. [ Используется для преобразования аргументов , указанных в вызове функции; см . ] Если пусто, функция не принимает аргументов. Список параметров, состоящий из одного безымянного параметра независимого типа , эквивалентен пустому списку параметров. За исключением этого особого случая, у параметра не должно быть типа . Если завершается многоточием или a , количество аргументов должно быть равно или превышать количество параметров, которые не имеют аргумента по умолчанию и не являются пакетами параметров функции. Там, где синтаксически правильно и где « » не является частью , « » является синонимом « ». [ ДекларацияNote: parameter-declaration-clause [expr.call]end noteparameter-declaration-clause void cv voidparameter-declaration-clause function parameter pack...abstract-declarator, ......Example:

int printf(const char*, ...);

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

printf("hello world");
printf("a=%d b=%d", a, b);

Однако первый аргумент должен иметь тип, который может быть преобразован в ] [ Стандартный заголовок содержит механизм доступа к аргументам, переданным с использованием многоточия (см. И ). ]const char* end exampleNote: <cstdarg> [expr.call] [support.runtime]end note

Одно имя может использоваться для нескольких различных функций в одной области; это функция overloading. Все объявления функции должны точно согласовываться как в типе возвращаемого значения, так и в списке типов-параметров. Тип функции определяется по следующим правилам. Тип каждого параметра (включая пакеты параметров функций) определяется его собственным decl-specifier-seq и declarator. После определения типа каждого параметра любой параметр типа «массив T» или типа функции T корректируется как «указатель на T». После создания списка типов параметров любые изменения верхнего уровня, cv-qualifiers изменяющие тип параметра, удаляются при формировании типа функции. Результирующий список преобразованных типов параметров и наличие или отсутствие многоточия или пакета параметров функции - это функция parameter-type-list. [ Note: Это преобразование не влияет на типы параметров. Например, int(*)(const int p, decltype(p)*) и int(*)(int, const int*) идентичные типы. ] end note

Тип функции с символом a cv-qualifier-seqили a ref-qualifier(включая тип с именем typedef-name([dcl.typedef], [temp.param])) должен отображаться только как:

[Example:

typedef int FIC(int) const;
FIC f;              // ill-formed: does not declare a member function
struct S {
  FIC f;            // OK
};
FIC S::*pm = &S::f; // OK

end example]

Эффект от a cv-qualifier-seq в деклараторе функции отличается от добавления cv-квалификации поверх типа функции. В последнем случае cv-квалификаторы игнорируются. [ Note: Тип функции, имеющий a cv-qualifier-seq, не является типом cv; не существует типов функций с квалификацией cv. ] [ end noteExample:

typedef void F();
struct S {
  const F f;        // OK: equivalent to: void f();
};

end example]

Тип возвращаемого значения, список типов-параметров, the ref-qualifier, the cv-qualifier-seqи спецификация исключения, но не the default arguments, являются частью типа функции. [ Note: Типы функций проверяются во время присвоения и инициализации указателей на функции, ссылок на функции и указателей на функции-члены. ]end note

[ ДекларацияExample:

int fseek(FILE*, long, int);

объявляет функцию, принимающую три аргумента указанных типов и возвращающую int ([dcl.type]). ]end example

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

Типы не должны определяться в возвращаемых типах или типах параметров. Тип параметра или возвращаемый тип для определения функции не должен быть неполным (возможно, cv-квалифицированным) типом класса в контексте определения функции, если только функция не является deleted.

Typedef типа функции может использоваться для объявления функции, но не должен использоваться define a function. [Example:

typedef void F();
F  fv;              // OK: equivalent to void fv();
F  fv { }           // ill-formed
void fv() { }       // OK: definition of fv

end example]

Идентификатор может быть дополнительно предоставлен как имя параметра; если присутствует в function definition, он называет параметр. [ Note: В частности, имена параметров также являются необязательными в определениях функций и именах, используемых для параметра в разных объявлениях, и определение функции не обязательно должно быть одинаковым. Если имя параметра присутствует в объявлении функции, которое не является определением, его нельзя использовать вне декларатора функции, потому что это степень его потенциальной области видимости ([basic.scope.proto]). ]end note

[ Example: Декларация

int i,
    *pi,
    f(),
    *fpi(int),
    (*pif)(const char*, const char*),
    (*fpif(int))(int);

объявляет целое число i, указатель pi на целое число, функцию f , не принимающую аргументов и возвращающую целое число, функцию, fpi принимающую целочисленный аргумент и возвращающую указатель на целое число, указатель pif на функцию, которая принимает два указателя на постоянные символы и возвращает целое число , функция, fpif принимающая целочисленный аргумент и возвращающая указатель на функцию, которая принимает целочисленный аргумент и возвращает целое число. Особенно полезно сравнивать fpi и pif. Привязка *fpi(int) is *(fpi(int)), поэтому объявление предполагает, и та же конструкция в выражении требует вызова функцииfpi, а затем использования косвенного обращения через результат (указатель) для получения целого числа. В деклараторе (*pif)(const char*, const char*)дополнительные круглые скобки необходимы, чтобы указать, что косвенное обращение через указатель на функцию приводит к функции, которая затем вызывается. ] [ Typedefs и иногда удобен, когда тип возвращаемого значения функции сложный. Например, указанная выше функция могла быть объявленаend exampleNote: trailing-return-types fpif

typedef int  IFUNC(int);
IFUNC*  fpif(int);

или

auto fpif(int)->int(*)(int);

A trailing-return-typeнаиболее полезен для типа, который было бы сложнее указать перед declarator-id:

template <class T, class U> auto add(T t, U u) -> decltype(t + u);

скорее, чем

template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);

end note]

A non-template function - это функция, которая не является специализацией шаблона функции. [ Note: Шаблон функции не является функцией. ] end note

A declarator-idили abstract-declarator содержащие многоточие должны использоваться только в parameter-declaration. Такой parameter-declarationвот parameter pack. Когда он является частью parameter-declaration-clause, пакет параметров - это function parameter pack. [ Note: В противном случае, parameter-declarationявляется частью a, template-parameter-listа пакет параметров является пакетом параметров шаблона; см [temp.param]. ] Пакет параметров функции - это файл . [ end note pack expansionExample:

template<typename... T> void f(T (* ...t)(int, int));

int add(int, int);
float subtract(int, int);

void g() {
  f(add, subtract);
}

end example]

Возникает синтаксическая двусмысленность, когда многоточие стоит в конце a parameter-declaration-clauseбез предшествующей запятой. В этом случае многоточие анализируется как часть, abstract-declaratorесли тип параметра либо называет пакет параметров шаблона, который не был развернут, либо содержит auto; в противном случае он анализируется как часть parameter-declaration-clause.101

Как указано в синтаксисе, cv-квалификаторы являются важным компонентом в типах возвращаемых функций.

Можно явно устранить неоднозначность синтаксического анализа, введя запятую (так что многоточие будет анализироваться как часть parameter-declaration-clause) или введя имя для параметра (так, чтобы многоточие было проанализировано как часть declarator-id).

11.3.6 Default arguments [dcl.fct.default]

Если initializer-clauseв a указано, parameter-declarationэто initializer-clause используется как аргумент по умолчанию. Аргументы по умолчанию будут использоваться в вызовах, в которых отсутствуют конечные аргументы.

[ Example: Декларация

void point(int = 3, int = 4);

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

point(1,2);  point(1);  point();

Последние два вызова эквивалентны point(1,4) и point(3,4)соответственно. ]end example

Аргумент по умолчанию должен быть указан только в parameter-declaration-clause объявлении функции lambda-declarator или в template-parameter; в последнем случае initializer-clauseдолжен быть assignment-expression. Аргумент по умолчанию не должен указываться для пакета параметров. Если это указано в a parameter-declaration-clause, это не должно происходить в пределах a declarator или abstract-declarator a parameter-declaration.102

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

void g(int = 0, ...);           // OK, ellipsis is not a parameter so it can follow
                                // a parameter with a default argument
void f(int, int);
void f(int, int = 7);
void h() {
  f(3);                         // OK, calls f(3, 7)
  void f(int = 1, int);         // error: does not use default from surrounding scope
}
void m() {
  void f(int, int);             // has no defaults
  f(4);                         // error: wrong number of arguments
  void f(int, int = 5);         // OK
  f(4);                         // OK, calls f(4, 5);
  void f(int, int = 5);         // error: cannot redefine, even to same value
}
void n() {
  f(6);                         // OK, calls f(6, 7)
}

end example] Для данной встроенной функции, определенной в разных единицах трансляции, накопленные наборы аргументов по умолчанию в конце единиц трансляции должны быть одинаковыми; см [basic.def.odr]. Если объявление друга указывает выражение аргумента по умолчанию, это объявление должно быть определением и должно быть единственным объявлением функции или шаблона функции в блоке перевода.

Аргумент по умолчанию имеет те же семантические ограничения, что и инициализатор в объявлении переменной типа параметра с использованием copy-initialization семантики. Имена в аргументе по умолчанию связываются, и семантические ограничения проверяются в точке, где появляется аргумент по умолчанию. Поиск имени и проверка семантических ограничений для аргументов по умолчанию в шаблонах функций и в функциях-членах шаблонов классов выполняются, как описано в [temp.inst]. [ Example: В следующем коде g будет вызываться со значением f(2):

int a = 1;
int f(int);
int g(int x = f(a));            // default argument: f(​::​a)

void h() {
  a = 2;
  {
  int a = 3;
  g();                          // g(f(​::​a))
  }
}

end example] [ Note: В объявлениях функций-членов ищутся имена в аргументах по умолчанию, как описано в [basic.lookup.unqual]. Проверка доступа применяется к именам в аргументах по умолчанию, как описано в разделе [class.access]. ]end note

За исключением функций-членов шаблонов классов, аргументы по умолчанию в определении функции-члена, которое появляется за пределами определения класса, добавляются к набору аргументов по умолчанию, предоставленных объявлением функции-члена в определении класса; программа имеет неправильный формат, если так объявлен конструктор по умолчанию ([class.ctor]), конструктор копирования или перемещения или оператор присваивания копирования или перемещения ([class.copy]). Аргументы по умолчанию для функции-члена шаблона класса должны быть указаны в начальном объявлении функции-члена в шаблоне класса. [Example:

class C {
  void f(int i = 3);
  void g(int i, int j = 99);
};

void C::f(int i = 3) {}         // error: default argument already specified in class scope
void C::g(int i = 88, int j) {} // in this translation unit, C​::​g can be called with no argument

end example]

Локальная переменная не должна появляться как потенциально оцениваемое выражение в аргументе по умолчанию. [Example:

void f() {
  int i;
  extern void g(int x = i);         // error
  extern void h(int x = sizeof(i)); // OK
  // ...
}

end example]

[ Note: Ключевое слово this может не появляться в аргументе по умолчанию функции-члена; см [expr.prim.this]. [Example:

class A {
  void f(A* p = this) { }           // error
};

end example] ]end note

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

int a;
int f(int a, int b = a);            // error: parameter a used as default argument
typedef int I;
int g(float I, int b = I(2));       // error: parameter I found
int h(int a, int b = sizeof(a));    // OK, unevaluated operand

end example] Член нестатический не должен появляться в аргументе по умолчанию , если он не появляется , как id-expressionиз class member access expression или , если он не используется для формирования указателя на член ([expr.unary.op]). [ Example: Объявление X​::​mem1() в следующем примере неверно сформировано, поскольку не предоставляется объект для нестатического члена, X​::​a используемого в качестве инициализатора.

int b;
class X {
  int a;
  int mem1(int i = a);              // error: non-static member a used as default argument
  int mem2(int i = b);              // OK;  use X​::​b
  static int b;
};

Однако объявление of X​::​mem2() имеет смысл, поскольку для доступа к статическому члену не требуется никаких объектов X​::​b. Классы, объекты и члены описаны в разделе [class]. ] Аргумент по умолчанию не является частью типа функции. [end exampleExample:

int f(int = 0);

void h() {
  int j = f(1);
  int k = f();                      // OK, means f(0)
}

int (*p1)(int) = &f;
int (*p2)() = &f;                   // error: type mismatch

end example] Когда объявление функции вводится через a using-declaration, также становится известна любая информация аргумента по умолчанию, связанная с объявлением. Если после этого функция повторно объявляется в пространстве имен с дополнительными аргументами по умолчанию, дополнительные аргументы также становятся известными в любой момент после повторного объявления, где using-declaration находится в области видимости.

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

struct A {
  virtual void f(int a = 7);
};
struct B : public A {
  void f(int a);
};
void m() {
  B* pb = new B;
  A* pa = pb;
  pa->f();          // OK, calls pa->B​::​f(7)
  pb->f();          // error: wrong number of arguments for B​::​f()
}

end example]

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

11.4 Function definitions [dcl.fct.def]

11.4.1 In general [dcl.fct.def.general]

Определения функций имеют вид

function-definition:
	attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt function-body
function-body:
	ctor-initializeropt compound-statement
	function-try-block
	= default ;
	= delete ;

Любую неформальную ссылку на тело функции следует интерпретировать как ссылку на нетерминал function-body. Необязательный параметр attribute-specifier-seqв a function-definition относится к функции. А virt-specifier-seqможет быть частью, function-definition только если это member-declaration.

В a function-definition, либо, либо должно быть правильно сформированным объявлением функции, как описано в . Функция должна быть определена только в пространстве имен или в области класса. void declarator ;declarator ; [dcl.fct]

[ Example: Простой пример полного определения функции:

int max(int a, int b, int c) {
  int m = (a > b) ? a : b;
  return (m > c) ? m : c;
}

Вот int это decl-specifier-seq; max(int a, int b, int c) это declarator; { /* ... */ } это function-body. ]end example

A ctor-initializer используется только в конструкторе; видеть [class.ctor] и [class.init].

[ Note: A cv-qualifier-seqвлияет на тип this в теле функции-члена; см [dcl.ref]. ]end note

[ Note: Неиспользуемые параметры называть не нужно. Например,

void print(int a, int) {
  std::printf("a = %d\n",a);
}

end note]

В function-body, a function-local predefined variable обозначает объект блочной области со статической продолжительностью хранения, которая неявно определена (см [basic.scope.block]. Раздел "Ресурсы" ).

Локальная для функции предопределенная переменная _­_­func_­_­определяется так, как если бы определение формы

static const char __func__[] = "function-name";

был предоставлен, где function-name - строка, определяемая реализацией. Не указано, имеет ли такая переменная адрес, отличный от адреса любого другого объекта в программе.103 [Example:

struct S {
  S() : s(__func__) { }             // OK
  const char* s;
};
void f(const char* s = __func__);   // error: _­_­func_­_­ is undeclared

end example]

Реализациям разрешено предоставлять дополнительные предопределенные переменные с соответствующими именами reserved to the implementation. Если предопределенной переменной нет odr-used, ее строковое значение не обязательно должно присутствовать в образе программы.

11.4.2 Explicitly-defaulted functions [dcl.fct.def.default]

Определение функции в форме:

attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt  = default ;

называется explicitly-defaulted определением. Функция, которая явно задана по умолчанию, должна

  • быть специальной функцией-членом,

  • иметь один и тот же объявленный тип функции (за исключением возможных различий ref-qualifiers и за исключением того, что в случае конструктора копирования или оператора присваивания копии тип параметра может быть «ссылкой на неконстантный T», где T является именем класса функции-члена) как если это было неявно объявлено, и

  • не иметь аргументов по умолчанию.

Явно заданная по умолчанию функция, которая не определена как удаленная, может быть объявлена constexpr только в том случае, если она была неявно объявлена ​​как constexpr. Если функция явно задана по умолчанию при ее первом объявлении, она неявно считается constexpr таковой, если неявное объявление будет.

Если функция, для которой явно задано значение по умолчанию, объявляется с помощью noexcept-specifier, который не создает такую ​​же спецификацию исключения, как неявное объявление ([except.spec]), тогда

  • если функция явно задана по умолчанию при ее первом объявлении, она определяется как удаленная;

  • в противном случае программа имеет неправильный формат.

[Example:

struct S {
  constexpr S() = default;              // ill-formed: implicit S() is not constexpr
  S(int a = 0) = default;               // ill-formed: default argument
  void operator=(const S&) = default;   // ill-formed: non-matching return type
  ~S() noexcept(false) = default;       // deleted: exception specification does not match
private:
  int i;
  S(S&);                                // OK: private copy constructor
};
S::S(S&) = default;                     // OK: defines copy constructor

end example]

Явно заданные по умолчанию функции и неявно объявленные функции совместно называются defaulted функциями, и реализация должна предоставлять для них неявные определения ([class.ctor] [class.dtor], [class.copy]), что может означать определение их как удаленных. Функция - user-provided это если она объявлена ​​пользователем и не была явно задана по умолчанию или удалена при ее первом объявлении. Предоставляемая пользователем функция с явно заданным по умолчанию значением (т. Е. Явно заданная по умолчанию после ее первого объявления) определяется в точке, где она явно задана по умолчанию; если такая функция неявно определена как удаленная, программа имеет неправильный формат. [ Note: Объявление функции по умолчанию после ее первого объявления может обеспечить эффективное выполнение и краткое определение, в то же время обеспечивая стабильный двоичный интерфейс для развивающейся базы кода. ]end note

[Example:

struct trivial {
  trivial() = default;
  trivial(const trivial&) = default;
  trivial(trivial&&) = default;
  trivial& operator=(const trivial&) = default;
  trivial& operator=(trivial&&) = default;
  ~trivial() = default;
};

struct nontrivial1 {
  nontrivial1();
};
nontrivial1::nontrivial1() = default;   // not first declaration

end example]

11.4.3 Deleted definitions [dcl.fct.def.delete]

Определение функции в форме:

attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt  = delete ;

называется а deleted definition. Функция с удаленным определением также называется функцией deleted function.

Программа, которая неявно или явно ссылается на удаленную функцию, за исключением ее объявления, плохо сформирована. [ Note: Это включает неявный или явный вызов функции и формирование указателя или указателя на член функции. Это применимо даже к ссылкам в выражениях, которые потенциально не оцениваются. Если функция перегружена, на нее ссылаются только в том случае, если функция выбрана разрешением перегрузки. Неявное odr-use выражение виртуальной функции само по себе не является ссылкой. ] end note

[ Example: Можно принудительно применить инициализацию не по умолчанию и нецелую инициализацию с помощью

struct onlydouble {
  onlydouble() = delete;                // OK, but redundant
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};

end example]

[ Example: Можно предотвратить использование класса в определенных случаях new-expressions , используя удаленные определения пользователя, объявленного operator new для этого класса.

struct sometype {
  void* operator new(std::size_t) = delete;
  void* operator new[](std::size_t) = delete;
};
sometype* p = new sometype;     // error, deleted class operator new
sometype* q = new sometype[3];  // error, deleted class operator new[]

end example]

[ Example: Можно сделать класс не копируемым, то есть доступным только для перемещения, используя удаленные определения конструктора копирования и оператора присваивания копии, а затем предоставив определения по умолчанию для конструктора перемещения и оператора присваивания перемещения.

struct moveonly {
  moveonly() = default;
  moveonly(const moveonly&) = delete;
  moveonly(moveonly&&) = default;
  moveonly& operator=(const moveonly&) = delete;
  moveonly& operator=(moveonly&&) = default;
  ~moveonly() = default;
};
moveonly* p;
moveonly q(*p);                 // error, deleted copy constructor

end example]

Удаленная функция неявно является файлом inline function. [ Применяется к удаленным определениям. ] Удаленное определение функции должно быть первым объявлением функции или, для явной специализации шаблона функции, первым объявлением этой специализации. Неявно объявленная функция выделения или освобождения ( ) не должна определяться как удаленная. [Note: one-definition rule end note[basic.stc.dynamic]Example:

struct sometype {
  sometype();
};
sometype::sometype() = delete;  // ill-formed; not first declaration

end example]

11.5 Structured binding declarations [dcl.struct.bind]

Структурированная связывании декларация вводит identifiers v0, v1, v2, ... из identifier-listкак названия ([basic.scope.declarative]), называется structured bindingс. Позвольте cv обозначить cv-qualifiers в decl-specifier-seq. Сначала вводится переменная с уникальным именем e . Если assignment-expressionв initializer имеет тип массива, A а не ref-qualifierприсутствует, e имеет тип, cv A и каждый элемент инициализируется копией или инициализируется напрямую из соответствующего элемента, assignment-expressionкак указано в форме initializer. В противном случае e определяется как если бы

attribute-specifier-seqopt decl-specifier-seq ref-qualifieropt e initializer ;

где объявление никогда не интерпретируется как объявление функции, а части объявления, кроме declarator-id, взяты из соответствующего объявления структурированной привязки. Тип id-expression e называется E. [ Note: E никогда не является ссылочным типом (пункт [expr]). ]end note

Если E это тип массива с типом элемента T, количество элементов в identifier-listдолжно быть равно количеству элементов E. Каждый vi - это имя lvalue, которое относится к элементу i массива и имеет тип T; ссылочный тип - T. [ Note: CV-квалификаторы верхнего уровня T are cv. ] [end noteExample:

  auto f() -> int(&)[2];
  auto [ x, y ] = f();          // x and y refer to elements in a copy of the array return value
  auto& [ xr, yr ] = f();       // xr and yr refer to elements in the array referred to by f's return value

end example]

В противном случае, если qualified-id std​::​tuple_­size<E> имена являются полным типом, выражение std​::​tuple_­size<E>​::​value должно быть правильно сформированным интегральным константным выражением, а количество элементов в identifier-listэлементе должно быть равно значению этого выражения. unqualified-id get Ищется в объеме E путем class member access lookup, и если , который находит по крайней мере , одна декларация, то инициализатор e.get<i>(). В противном случае инициализатор находится там get<i>(e), где get ищется в associated namespaces. В любом случае get<i> интерпретируется как template-id. [ Note: Обычный unqualified lookup не выполняется. ] В любом случае является lvalue, если тип объекта является ссылкой lvalue, и xvalue в противном случае. Принимая во внимание типа обозначен , каждый представляет собой переменную типа «ссылка на » инициализируется инициализатор, где ссылка является ссылкой , если именующим инициализатор является Левым и ссылкой на Rvalue в противном случае; ссылочный тип - .end note e e Ti std​::​tuple_­element<i, E>​::​type vi Ti Ti

В противном случае все Eнестатические элементы данных должны быть общедоступными прямыми членами одного E и того же однозначного общедоступного базового класса E, E не должны иметь анонимного члена объединения, а количество элементов в классе identifier-listдолжно быть равно количеству нестатических элементов. -статические данные членов E. Обозначая нестатические элементы данных , E как m0, m1, m2, ... (в порядке декларации), каждый vi является именем именующего , который относится к элементу mi из e и типа которого является cv Ti, где Ti находится объявленный тип этого члена; ссылочный тип - cv Ti. Lvalue является битовым полем, если этот член является битовым полем. [Example:

struct S { int x1 : 2; volatile double y1; };
S f();
const auto [ x, y ] = f();

Тип id-expression x - «const int», тип id-expression y - «const volatile double». ]end example

11.6 Initializers [dcl.init]

Заявитель может указать начальное значение для объявляемого идентификатора. Идентификатор обозначает инициализируемую переменную. Процесс инициализации, описанный в оставшейся части, [dcl.init] применим также к инициализациям, указанным в других синтаксических контекстах, таких как инициализация параметров функции ([expr.call]) или инициализация возвращаемых значений ([stmt.return]).

initializer:
	brace-or-equal-initializer
	( expression-list )
brace-or-equal-initializer:
	= initializer-clause
	braced-init-list
initializer-clause:
	assignment-expression
	braced-init-list
initializer-list:
	initializer-clause ...opt
	initializer-list , initializer-clause ...opt
braced-init-list:
	{ initializer-list ,opt }
	{ }
expr-or-braced-init-list:
	expression
	braced-init-list

За исключением объектов, объявленных с помощью constexpr спецификатора, для которых см. [dcl.constexpr], initializerВ определении переменной может состоять из произвольных выражений, включающих литералы и ранее объявленные переменные и функции, независимо от продолжительности хранения переменной. [Example:

int f(int);
int a = 2;
int b = f(a);
int c(b);

end example]

[ Note: Аргументы по умолчанию более ограничены; см [dcl.fct.default]. ]end note

[ Note: Порядок инициализации переменных со статической продолжительностью хранения описан в [basic.start] и [stmt.dcl]. ]end note

Неправильно сформировано объявление переменной области видимости блока с внешней или внутренней связью, имеющей initializerсимвол.

К zero-initialize объекту или ссылке типа T означает:

  • если T равно a scalar type, объект инициализируется значением, полученным преобразованием целочисленного литерала 0 (ноль) в T;104

  • если T является (возможно, cv-квалифицированным) типом класса без объединения, каждый нестатический член данных, каждый подобъект невиртуального базового класса, и, если объект не является подобъектом базового класса, каждый подобъект виртуального базового класса равен нулю. инициализируется и заполнение инициализируется нулевыми битами;

  • если T тип объединения (возможно, квалифицированный cv), первый нестатический именованный член данных объекта инициализируется нулем, а заполнение инициализируется нулевыми битами;

  • если T это тип массива, каждый элемент инициализируется нулем;

  • если T это ссылочный тип, инициализация не выполняется.

Для default-initialize объекта типа T означает:

  • Если T является (возможно, cv-квалифицируемым) class type, рассматриваются конструкторы. Применимые конструкторы перечисляются ([over.match.ctor]), а лучший для initializer () них выбирается с помощью overload resolution. Выбранный таким образом конструктор вызывается с пустым списком аргументов для инициализации объекта.

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

  • В противном случае инициализация не выполняется.

Тип класса T - это const-default-constructible если инициализация по умолчанию T вызовет предоставленный пользователем конструктор T (не унаследованный от базового класса) или если

  • каждый прямой невариантный нестатический член M данных T имеет инициализатор члена по умолчанию или, если он M имеет тип класса X (или его массив), X является константным по умолчанию-конструктивным,

  • если T это объединение по крайней мере с одним нестатическим членом данных, ровно один вариантный член имеет инициализатор члена по умолчанию,

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

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

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

Для value-initialize объекта типа T означает:

  • если T это (возможно, cv-квалифицированный) class type либо без конструктора по умолчанию ([class.ctor]), либо конструктор по умолчанию, предоставленный или удаленный пользователем, то объект инициализируется по умолчанию;

  • если T это (возможно, cv-квалифицированный) тип класса без предоставленного пользователем или удаленного конструктора по умолчанию, тогда объект инициализируется нулем и проверяются семантические ограничения для инициализации по умолчанию, а если T имеет нетривиальный конструктор по умолчанию, объект объект инициализирован по умолчанию;

  • если T тип массива, то каждый элемент инициализируется значением;

  • в противном случае объект инициализируется нулем.

Программа, которая вызывает инициализацию по умолчанию или инициализацию значения объекта ссылочного типа, плохо сформирована.

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

Объект, инициализатором которого является пустой набор круглых скобок, т. Е. (), Должен быть инициализирован значением.

[ Note: Поскольку () это не разрешено синтаксисом initializer,

X a();

не является объявлением объекта класса X, а объявлением функции, не принимающей аргументов и возвращающей X. Форма () допускается в некоторых других контекстах инициализации ([expr.new], [expr.type.conv], [class.base.init]). ]end note

Если для объекта не указан инициализатор, объект инициализируется по умолчанию. Когда получается хранилище для объекта с автоматической или динамической продолжительностью хранения, объект имеет indeterminate value, и если для объекта не выполняется инициализация, этот объект сохраняет неопределенное значение до тех пор, пока это значение не будет заменено ([expr.ass]). [ Note: Объекты со статической продолжительностью хранения или хранением потоков инициализируются нулем, см [basic.start.static]. ] Если в результате оценки получено неопределенное значение, поведение не определено, за исключением следующих случаев: end note

  • Если неопределенное значение unsigned narrow character type или std​::​byte type ([cstddef.syn]) получено в результате оценки:

    тогда результат операции - неопределенное значение.

  • Если неопределенное значение беззнакового узкого символьного типа или std​::​byte типа получается путем оценки правого операнда простого assignment operator , первый операнд которого является l-значением беззнакового узкого символьного типа или std​::​byte типа, неопределенное значение заменяет значение объекта, на который ссылается левый операнд.

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

  • Если неопределенное значение беззнакового узкого символьного типа или std​::​byte типа создается путем оценки выражения инициализации при инициализации объекта std​::​byte типа, этот объект инициализируется неопределенным значением.

[Example:

  int f(bool b) {
    unsigned char c;
    unsigned char d = c;        // OK, d has an indeterminate value
    int e = d;                  // undefined behavior
    return b ? d : 0;           // undefined behavior if b is true
  }

end example]

Инициализатор для статического члена находится в области действия класса члена. [Example:

int a;

struct X {
  static int a;
  static int b;
};

int X::a = 1;
int X::b = a;                   // X​::​b = X​::​a

end example]

Если инициализируемая сущность не имеет типа класса, expression-listинициализатор в скобках должен быть единственным выражением.

Инициализации , которая происходит в = виде brace-or-equal-initializerили condition([stmt.select]), а также в передачи аргументов, возврата функции, throwing an exception, handling an exceptionи aggregate member initialization, называется copy-initialization. [ Note: Инициализация копирования может вызвать move ([class.copy]). ] end note

Инициализация, которая происходит в формах

T x(a);
T x{a};

а также в new expressions, static_­cast expressions, functional notation type conversions, mem-initializers, и braced-init-listформа condition называется direct-initialization.

Семантика инициализаторов следующая. destination type Является тип объекта или ссылки инициализации , и source type является типом выражения инициализатора. Если инициализатор не является одним (возможно, заключенным в скобки) выражением, исходный тип не определен.

  • Если инициализатором является (без скобок) braced-init-list или есть , то объект или ссылка - . = braced-init-list list-initialized

  • Если тип назначения является ссылочным, см [dcl.init.ref].

  • Если тип назначения является массивом символов, массивом char16_­t, массивом char32_­tили массивом wchar_­t, а инициализатором является строковый литерал, см [dcl.init.string].

  • Если инициализатор есть (), объект инициализируется значением.

  • В противном случае, если целевым типом является массив, программа имеет неправильный формат.

  • Если тип назначения является типом класса (возможно, квалифицированным cv):

    • Если выражение инициализатора является prvalue, а версия cv-unqualified исходного типа является тем же классом, что и класс назначения, выражение инициализатора используется для инициализации объекта назначения. [ Example: T x = T(T(T())); вызывает T конструктор по умолчанию для инициализации x. ] end example

    • В противном случае, если инициализация является прямой инициализацией или если это инициализация копированием, где cv-неквалифицированная версия исходного типа является тем же классом, что и класс назначения, или производным классом от него, рассматриваются конструкторы. Применимые конструкторы перечисляются ([over.match.ctor]), и лучший из них выбирается overload resolution. Выбранный таким образом конструктор вызывается для инициализации объекта с помощью выражения инициализатора или expression-listего аргументов. Если конструктор не применяется или разрешение перегрузки неоднозначно, инициализация сформирована неправильно.

    • В противном случае (т. Е. Для остальных случаев инициализации копирования) определяемые пользователем последовательности преобразования, которые могут преобразовывать из исходного типа в целевой тип или (когда используется функция преобразования) в его производный класс, перечисляются, как описано в [over.match.copy], и выбирается лучший overload resolution. Если преобразование не может быть выполнено или неоднозначно, инициализация сформирована неправильно. Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов является prvalue неквалифицированной cv версии целевого типа, чей объект результата инициализируется конструктором. Вызов используется для прямой инициализации в соответствии с приведенными выше правилами объекта, который является адресатом инициализации копии.

  • В противном случае, если исходный тип является типом класса (возможно, квалифицированным cv), рассматриваются функции преобразования. Применимые функции преобразования перечислены в перечислении ([over.match.conv]), и лучшая из них выбирается с помощью overload resolution. Выбранное таким образом пользовательское преобразование вызывается для преобразования выражения инициализатора в инициализируемый объект. Если преобразование не может быть выполнено или неоднозначно, инициализация сформирована неправильно.

  • В противном случае начальным значением инициализируемого объекта является (возможно, преобразованное) значение выражения инициализатора. Standard conversions при необходимости будет использоваться для преобразования выражения инициализатора в неквалифицированную версию типа назначения cv; никакие пользовательские преобразования не учитываются. Если преобразование не может быть выполнено, инициализация выполнена неправильно. При инициализации битового поля значением, которое оно не может представить, результирующее значение битового поля определяется реализацией. [ Note: Выражение типа «cv1 T» может инициализировать объект типа «cv2 T» независимо от cv-квалификаторов cv1 и cv2.

    int a;
    const int b = a;
    int c = b;

    end note]

initializer-clauseС последующим многоточием является pack expansion.

Если инициализатор заключен в круглые скобки expression-list, выражения вычисляются в порядке, указанном для function calls.

Объект, инициализация которого завершена, считается построенным, даже если для инициализации не вызывается конструктор класса объекта. [ Note: Такой объект может быть значение инициализирован или инициализирован aggregate initialization или с помощью inherited constructor. ]end note

Объявление, которое определяет инициализацию переменной, будь то явным инициализатором или инициализацией по умолчанию, называется объявлением initializing declaration этой переменной. [ Note: В большинстве случаев это определяющее объявление ([basic.def]) переменной, но инициализирующее объявление не встроенного статического элемента данных ([class.static.data]) может быть объявлением в определении класса, а не определением в области пространства имен. ]end note

Как указано в [conv.ptr], преобразование целочисленного литерала, значение которого имеет 0 тип указателя, приводит к значению нулевого указателя.

11.6.1 Aggregates [dcl.init.aggr]

An aggregate - это массив или class с

  • нет предоставленных пользователем explicitили унаследованных конструкторов ([class.ctor]),

  • нет частных или защищенных нестатических элементов данных (пункт [class.access]),

  • нет virtual functions, и

  • нет виртуальных, частных или защищенных базовых классов ([class.mi]).

[ Note: Агрегированная инициализация не позволяет получить доступ к защищенным и закрытым членам или конструкторам базового класса. ]end note

В elements состав агрегата входят:

  • для массива элементы массива в порядке возрастания индекса, или

  • для класса - прямые базовые классы в порядке объявления, за которыми следуют прямые нестатические элементы данных ([class.mem]), которые не являются членами анонимного объединения, в порядке объявления.

Когда агрегат инициализируется списком инициализаторов, как указано в [dcl.init.list], элементы списка инициализаторов принимаются в качестве инициализаторов для элементов агрегата по порядку. Каждый элемент инициализируется копией из соответствующего initializer-clause. Если initializer-clause- это выражение, а narrowing conversion для преобразования выражения требуется a , программа имеет неправильный формат . [ Note: Если initializer-clauseсам является списком инициализатора, элемент инициализируется списком, что приведет к рекурсивному применению правил в этом разделе, если элемент является агрегатом. ] [ end noteExample:

struct A {
  int x;
  struct B {
    int i;
    int j;
  } b;
} a = { 1, { 2, 3 } };

инициализируется a.x 1, a.b.i 2, a.b.j 3.

struct base1 { int b1, b2 = 42; };
struct base2 {
  base2() {
    b3 = 42;
  }
  int b3;
};
struct derived : base1, base2 {
  int d;
};

derived d1{{1, 2}, {}, 4};
derived d2{{}, {}, 4};

инициализируется d1.b1 с 1, d1.b2 с 2, d1.b3 с 42, d1.d с 4 и d2.b1 с 0, d2.b2 с 42, d2.b3 с 42, d2.d с 4. ]end example

Агрегат, являющийся классом, также может быть инициализирован одним выражением, не заключенным в фигурные скобки, как описано в [dcl.init].

Массив с неизвестной границей, инициализированный заключенным в фигурные скобки initializer-list содержащим , где должно быть больше нуля, определяется как имеющий elements ( ). [n initializer-clausesnn[dcl.array]Example:

int x[] = { 1, 3, 5 };

объявляется и инициализируется x как одномерный массив, состоящий из трех элементов, поскольку размер не был указан и имеется три инициализатора. ] Пустой список инициализаторов не должен использоваться в качестве массива с неизвестной границей. [ Инициализатор члена по умолчанию не определяет границу для массива элементов с неизвестной границей. Поскольку инициализатор члена по умолчанию игнорируется, если присутствует подходящий ( ), инициализатор члена по умолчанию не считается инициализирующим массив с неизвестной границей. [end example{}initializer-clause105Note: mem-initializer[class.base.init]Example:

struct S {
  int y[] = { 0 };          // error: non-static data member of incomplete type
};

end example] ]end note

[ Note: Статические элементы данных и безымянные битовые поля не считаются элементами агрегата. [Example:

struct A {
  int i;
  static int s;
  int j;
  int :17;
  int k;
} a = { 1, 2, 3 };

Здесь инициализируется второй инициализатор 2, a.j а не статический элемент данных A​::​s, и инициализируется третий инициализатор 3, a.k а не безымянное битовое поле перед ним. ] ]end exampleend note

initializer-list Плохо сформированный , если число initializer-clauses превышает количество элементов , чтобы инициализировать. [Example:

char cv[4] = { 'a', 's', 'd', 'f', 0 };     // error

плохо сформирован. ]end example

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

  • Если элемент имеет инициализатор члена по умолчанию ([class.mem]), элемент инициализируется этим инициализатором.

  • В противном случае, если элемент не является ссылкой, элемент инициализируется копией из пустого списка инициализаторов ([dcl.init.list]).

  • В противном случае программа имеет неверный формат.

Если агрегат является объединением, а список инициализаторов пуст, тогда

  • если какой-либо вариантный член имеет инициализатор члена по умолчанию, этот член инициализируется из его инициализатора члена по умолчанию;

  • в противном случае первый член объединения (если есть) инициализируется копией из пустого списка инициализаторов.

[Example:

struct S { int a; const char* b; int c; int d = b[a]; };
S ss = { 1, "asdf" };

инициализируется ss.a с 1, ss.b с "asdf", ss.c значением выражения формы int{} (то есть, 0), и ss.d значением ss.b[ss.a] (то есть, 's'), и в

struct X { int i, j, k = 42; };
X a[] = { 1, 2, 3, 4, 5, 6 };
X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } };

a и b иметь такое же значение ]end example

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

  struct A;
  extern A a;
  struct A {
    const A& a1 { A{a,a} };     // OK
    const A& a2 { A{} };        // error
  };
  A a{a,a};                     // OK

end example]

Если совокупный класс C содержит субагрегатный элемент e без элементов, initializer-clausefor e не должен быть опущен в initializer-listдля объекта типа, C если также не пропущены initializer-clauses для всех C следующих элементов e . [Example:

struct S { } s;
struct A {
  S s1;
  int i1;
  S s2;
  int i2;
  S s3;
  int i3;
} a = {
  { },              // Required initialization
  0,
  s,                // Required initialization
  0
};                  // Initialization not required for A​::​s3 because A​::​i3 is also not initialized

end example]

При инициализации многомерного массива initializer-clauses элементы инициализируются с последним (крайним правым) индексом массива, изменяющим самый быстрый ([dcl.array]). [Example:

int x[2][2] = { 3, 1, 4, 2 };

инициализируется x[0][0] в 3, x[0][1] в 1, x[1][0] в 4и x[1][1] в 2. С другой стороны,

float y[4][3] = {
  { 1 }, { 2 }, { 3 }, { 4 }
};

инициализирует первый столбец y (рассматриваемого как двумерный массив), а остальные оставляет равными нулю. ]end example

Скобки можно опустить initializer-list следующим образом. Если initializer-list начинается с левой фигурной скобки, то следующий разделенный запятыми список initializer-clauses инициализирует элементы подагрегата; ошибочно, что существует нечто большее, initializer-clauses чем элементы. Если, однако, initializer-list для субагрегата не начинается с левой фигурной скобки, тогда initializer-clauses из списка берется только достаточно для инициализации элементов субагрегата; любые оставшиеся initializer-clauses остаются для инициализации следующего элемента агрегата, элементом которого является текущий подагрегат. [Example:

float y[4][3] = {
  { 1, 3, 5 },
  { 2, 4, 6 },
  { 3, 5, 7 },
};

является полностью-приготовился инициализации: 1, 3, 5 и инициализировать первую строку массива y[0], а именно y[0][0], y[0][1], и y[0][2]. Аналогичным образом следующие две строки инициализируют y[1] и y[2]. Инициализатор завершается раньше, и поэтому y[3]элементы s инициализируются, как если бы они были явно инициализированы выражением формы float(), то есть инициализируются с помощью 0.0. В следующем примере фигурные скобки initializer-list опущены; однако initializer-list имеет тот же эффект, что и полностью закрепленный initializer-list в приведенном выше примере,

float y[4][3] = {
  1, 3, 5, 2, 4, 6, 3, 5, 7
};

Инициализатор fory начинается с левой фигурной скобки, а инициализатор for - y[0] нет, поэтому используются три элемента из списка. Аналогичным образом следующие три берутся последовательно за y[1] и y[2]. ]end example

Все неявные преобразования типов (Clause [conv]) учитываются при инициализации элемента с помощью assignment-expression. Если assignment-expression может инициализировать элемент, он инициализируется. В противном случае, если элемент сам является субагрегатом, предполагается исключение фигурных скобок и assignment-expression рассматривается для инициализации первого элемента субагрегата. [ Note: Как указано выше, исключение скобок не может применяться к субагрегатам без элементов; требуется initializer-clauseдля всего подобъекта. ]end note

[Example:

struct A {
  int i;
  operator int();
};
struct B {
  A a1, a2;
  int z;
};
A a;
B b = { 4, a, a };

Брекеты опускаются во всем initializer-clause для b.a1.i. b.a1.i инициализируется с помощью 4, b.a2 инициализируется с помощью a, b.z инициализируется любым a.operator int() возвратом. ]end example

[ Note: Агрегатный массив или агрегатный класс могут содержать элементы типа класса с предоставленным пользователем конструктором ([class.ctor]). Инициализация этих агрегированных объектов описана в [class.expl.init]. ]end note

[ Note: Ли инициализация агрегатов со статической продолжительностью хранения статический или динамический задается в [basic.start.static], [basic.start.dynamic]и [stmt.dcl]. ]end note

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

union u { int a; const char* b; };
u a = { 1 };
u b = a;
u c = 1;                        // error
u d = { 0, "asdf" };            // error
u e = { "asdf" };               // error

end example]

[ Note: Как описано выше, фигурные скобки вокруг initializer-clause члена объединения можно опустить, если объединение является членом другого агрегата. ]end note

Синтаксис предусматривает пустые initializer-lists, но, тем не менее, C ++ не имеет массивов нулевой длины.

11.6.2 Character arrays [dcl.init.string]

Массив narrow character type, char16_­t массив, char32_­t массив или wchar_­t массив может быть инициализирован узким строковым литералом, char16_­t строковым литералом, char32_­t строковым литералом или широким строковым литералом, соответственно, или строковым литералом соответствующего типа, заключенным в фигурные скобки ([lex.string]). Последовательные символы значения строкового литерала инициализируют элементы массива. [Example:

char msg[] = "Syntax error on line %s\n";

показывает массив символов, члены которого инициализированы с помощью string-literal. Обратите внимание, что, поскольку '\n' это один символ, и поскольку'\0' добавляется завершающий символ , sizeof(msg) это 25. ]end example

Инициализаторов не должно быть больше, чем элементов массива. [Example:

char cv[4] = "asdf";            // error

неправильно сформирован, поскольку нет места для подразумеваемого трейлинга '\0'. ]end example

Если инициализаторов меньше, чем элементов массива, каждый элемент, не инициализированный явно, должен быть zero-initialized.

11.6.3 References [dcl.init.ref]

Переменная с объявленным типом “reference to type T должна быть инициализирована. [Example:

int g(int) noexcept;
void f() {
  int i;
  int& r = i;                   // r refers to i
  r = 1;                        // the value of i becomes 1
  int* p = &r;                  // p points to i
  int& rr = r;                  // rr refers to what r refers to, that is, to i
  int (&rg)(int) = g;           // rg refers to the function g
  rg(i);                        // calls function g
  int a[3];
  int (&ra)[3] = a;             // ra refers to the array a
  ra[1] = i;                    // modifies a[1]
}

end example]

Ссылку нельзя изменить для ссылки на другой объект после инициализации. [ Note: Присвоение ссылке присваивается объекту, на который ссылается ссылка ([expr.ass]). ] и являются инициализациями.end noteArgument passing function value return

Инициализатор может быть опущен для ссылки только в объявлении параметра ([dcl.fct]), в объявлении типа возвращаемого значения функции, в объявлении члена класса в его определении класса ([class.mem]) и там, где extern спецификатор используется явно. [Example:

int& r1;                        // error: initializer missing
extern int& r2;                 // OK

end example]

Для заданных типов «cv1 T1» и «cv2 T2», «cv1 T1» reference-related соответствует «cv2 T2», если T1 является тем же типом T2, что иT1 базовый класс , или является его базовым классом T2. «cv1 T1» reference-compatible С «cv2 T2», если

  • T1 относится к ссылке T2, или

  • T2 - это «noexcept функция» и T1 «функция», где типы функций в остальном одинаковы,

и cv1 одно и то же резюме-квалификация , как, или больше резюме , чем-квалификация, cv2. Во всех случаях, когда связь двух типов, связанная со ссылкой или совместимая со ссылкой, используется для установления действительности привязки ссылки и T1 является базовым классом T2, программа, которая требует такой привязки, плохо сформирована, если T1 является inaccessible или неоднозначной. ([class.member.lookup]) базовый класс T2.

Ссылка на тип «cv1 T1» инициализируется выражением типа «cv2 T2» следующим образом:

  • Если ссылка является ссылкой lvalue и выражением инициализатора

    • является lvalue (но не битовым полем), а «cv1 T1» совместим со ссылкой с «cv2 T2», или

    • имеет тип класса (т. е. T2 является типом класса), где T1 он не связан со ссылкой T2, и может быть преобразован в lvalue типа «cv3 T3», где «cv1 T1» совместим со ссылкой с «cv3 T3»106 (это преобразование выбирается путем перечисления применимые функции преобразования ([over.match.ref]) и выбор лучшей через overload resolution),

    тогда ссылка привязывается к выражению инициализатора lvalue в первом случае и к результату lvalue преобразования во втором случае (или, в любом случае, к соответствующему подобъекту базового класса объекта). [ Note: Обычные lvalue-to-rvalue, array-to-pointerи function-to-pointer стандартные преобразования не требуются и поэтому подавляются, когда выполняются такие прямые привязки к lvalue. ]end note

    [Example:

    double d = 2.0;
    double& rd = d;                 // rd refers to d
    const double& rcd = d;          // rcd refers to d
    
    struct A { };
    struct B : A { operator int&(); } b;
    A& ra = b;                      // ra refers to A subobject in b
    const A& rca = b;               // rca refers to A subobject in b
    int& ir = B();                  // ir refers to the result of B​::​operator int&
    

    end example]

  • В противном случае ссылка должна быть ссылкой lvalue на энергонезависимый константный тип (т. Е. cv1 Должна быть const), или ссылка должна быть ссылкой на rvalue. [Example:

    double& rd2 = 2.0;              // error: not an lvalue and reference not const
    int  i = 2;
    double& rd3 = i;                // error: type mismatch and reference not const
    

    end example]

    • Если выражение инициализатора

      • является rvalue (но не битовым полем) или функцией lvalue и «cv1 T1» совместим со ссылкой с «cv2 T2», или

      • имеет тип класса (т. е. T2 является типом класса), где T1 он не связан со ссылкой T2, и может быть преобразован в rvalue или функцию lvalue типа «cv3 T3», где «cv1 T1» совместим по ссылке с «cv3 T3» (см. [over.match.ref]),

      тогда значение выражения инициализатора в первом случае и результат преобразования во втором случае называется преобразованным инициализатором. Если преобразованный инициализатор является prvalue, его тип изменяется T4 на тип «cv1 T4» ([conv.qual]) и temporary materialization conversion применяется. В любом случае ссылка привязана к результирующему значению glvalue (или соответствующему подобъекту базового класса).

      [Example:

      struct A { };
      struct B : A { } b;
      extern B f();
      const A& rca2 = f();                // bound to the A subobject of the B rvalue.
      A&& rra = f();                      // same as above
      struct X {
        operator B();
        operator int&();
      } x;
      const A& r = x;                     // bound to the A subobject of the result of the conversion
      int i2 = 42;
      int&& rri = static_cast<int&&>(i2); // bound directly to i2
      B&& rrb = x;                        // bound directly to the result of operator B
      

      end example]

    • Иначе:

      • Если T1 или T2 является типом класса и T1 не является ссылкой , связанных с T2, определяемые пользователем преобразования рассматриваются с использованием правил для копирования-инициализации объекта типа «cv1 T1» с помощью пользовательского преобразования ([dcl.init], [over.match.copy], [over.match.conv]); программа будет неправильно сформирована, если соответствующая не ссылочная инициализация копии будет неправильно сформирована. Результат вызова функции преобразования, как описано для инициализации копии без ссылки, затем используется для прямой инициализации ссылки. Для этой прямой инициализации определенные пользователем преобразования не рассматриваются.

      • В противном случае выражение инициализатора неявно преобразуется в prvalue типа «cv1 T1». Применяется временное преобразование материализации, и ссылка привязывается к результату.

      Если T1 ссылка связана с T2:

      • cv1 должно быть такое же резюме, что и, или более высокое резюме, чем cv2,; а также

      • если ссылка является ссылкой rvalue, выражение инициализатора не должно быть lvalue.

      [Example:

      struct Banana { };
      struct Enigma { operator const Banana(); };
      struct Alaska { operator Banana&(); };
      void enigmatic() {
        typedef const Banana ConstBanana;
        Banana &&banana1 = ConstBanana(); // ill-formed
        Banana &&banana2 = Enigma();      // ill-formed
        Banana &&banana3 = Alaska();      // ill-formed
      }
      
      const double& rcd2 = 2;         // rcd2 refers to temporary with value 2.0
      double&& rrd = 2;               // rrd refers to temporary with value 2.0
      const volatile int cvi = 1;
      const int& r2 = cvi;            // error: cv-qualifier dropped
      struct A { operator volatile int&(); } a;
      const int& r3 = a;              // error: cv-qualifier dropped
                                      // from result of conversion function
      double d2 = 1.0;
      double&& rrd2 = d2;             // error: initializer is lvalue of related type
      struct X { operator int&(); };
      int&& rri2 = X();               // error: result of conversion function is lvalue of related type
      int i3 = 2;
      double&& rrd3 = i3;             // rrd3 refers to temporary with value 2.0
      

      end example]

Во всех случаях, кроме последнего (т. Е. Неявного преобразования выражения инициализатора в базовый тип ссылки), ссылка называется bind directly на выражение инициализатора.

[ Note: [class.temporary] описывает время жизни временных файлов, привязанных к ссылкам. ]end note

Это требует conversion function возврата ссылочного типа.

11.6.4 List-initialization [dcl.init.list]

List-initialization инициализация объекта или ссылки из braced-init-list. Такой инициализатор называется initializer list, а список, разделенный initializer-clauses запятыми, называется elements списком инициализаторов. Список инициализаторов может быть пустым. Инициализация списка может происходить в контекстах прямой инициализации или копирования-инициализации; Вызывается инициализация списка в контексте прямой инициализации direct-list-initialization и инициализация списка в контексте инициализации копирования copy-list-initialization. [ Note: Можно использовать инициализацию списка

[Example:

int a = {1};
std::complex<double> z{1,2};
new std::vector<std::string>{"once", "upon", "a", "time"};  // 4 string elements
f( {"Nicholas","Annemarie"} );  // pass list of two elements
return { "Norah" };             // return list of one element
int* e {};                      // initialization to zero / null pointer
x = double{1};                  // explicitly construct a double
std::map<std::string,int> anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} };

end example] ] end note

Конструктор - это объект, initializer-list constructor если его первый параметр имеет тип std​::​initializer_­list<E> или ссылку на возможно cv-квалификацию std​::​initializer_­list<E> для некоторого типа E, и либо нет других параметров, либо все остальные параметры имеют default arguments. [ Note: Конструкторы списка инициализаторов предпочтительнее других конструкторов в list-initialization ([over.match.list]). Передача списка инициализаторов в качестве аргумента в шаблон template<class T> C(T) конструктора класса C не создает конструктор списка инициализаторов, поскольку аргумент списка инициализаторов приводит к тому, что соответствующий параметр становится невыведенным context ([temp.deduct.call]). ] Шаблон не задан заранее; если заголовок не включен до использования - даже при неявном использовании, в котором указан тип - программа имеет неправильный формат . end notestd​::​initializer_­list <initializer_­list> std​::​initializer_­list not named

Список-инициализация объекта или ссылки типа T определяется следующим образом:

  • Если T это совокупный класс, а список инициализаторов имеет единственный элемент типа cv U, где U is T или класс, производный от T, объект инициализируется из этого элемента (путем копирования-инициализации для инициализации списка-копирования или путем прямой инициализации для прямого -лист-инициализация).

  • В противном случае, если T это массив символов, а в списке инициализаторов есть единственный элемент, который является строковым литералом соответствующего типа ([dcl.init.string]), инициализация выполняется, как описано в этом разделе.

  • В противном случае, если T является агрегатом, aggregate initialization выполняется.

    [Example:

    double ad[] = { 1, 2.0 };           // OK
    int ai[] = { 1, 2.0 };              // error: narrowing
    
    struct S2 {
      int m1;
      double m2, m3;
    };
    S2 s21 = { 1, 2, 3.0 };             // OK
    S2 s22 { 1.0, 2, 3 };               // error: narrowing
    S2 s23 { };                         // OK: default to 0,0,0
    

    end example]

  • В противном случае, если список инициализаторов не имеет элементов и T является типом класса с конструктором по умолчанию, объект инициализируется значением.

  • В противном случае, если T является специализацией std​::​initializer_­list<E>, объект создается , как описано ниже.

  • В противном случае, если T это тип класса, рассматриваются конструкторы. Применимые конструкторы перечислены, и лучший из них выбирается с помощью разрешения перегрузки ([over.match], [over.match.list]). Если для преобразования любого из аргументов требуется сужающее преобразование (см. Ниже), программа имеет неправильный формат.

    [Example:

    struct S {
      S(std::initializer_list<double>); // #1
      S(std::initializer_list<int>);    // #2
      S();                              // #3
      // ...
    };
    S s1 = { 1.0, 2.0, 3.0 };           // invoke #1
    S s2 = { 1, 2, 3 };                 // invoke #2
    S s3 = { };                         // invoke #3
    

    end example]

    [Example:

    struct Map {
      Map(std::initializer_list<std::pair<std::string,int>>);
    };
    Map ship = {{"Sophie",14}, {"Surprise",28}};

    end example]

    [Example:

    struct S {
      // no initializer-list constructors
      S(int, double, double);           // #1
      S();                              // #2
      // ...
    };
    S s1 = { 1, 2, 3.0 };               // OK: invoke #1
    S s2 { 1.0, 2, 3 };                 // error: narrowing
    S s3 { };                           // OK: invoke #2
    

    end example]

  • В противном случае, если T это перечисление с фиксированным базовым типом ([dcl.enum]), initializer-listимеет единственный элемент v, а инициализация - это прямая инициализация списка, объект инициализируется значением T(v) ([expr.type.conv]); если для преобразования v в базовый тип требуется сужающее преобразование T, программа имеет неправильный формат. [Example:

    enum byte : unsigned char { };
    byte b { 42 };                      // OK
    byte c = { 42 };                    // error
    byte d = byte{ 42 };                // OK; same value as b
    byte e { -1 };                      // error
    
    struct A { byte b; };
    A a1 = { { 42 } };                  // error
    A a2 = { byte{ 42 } };              // OK
    
    void f(byte);
    f({ 42 });                          // error
    
    enum class Handle : uint32_t { Invalid = 0 };
    Handle h { 42 };                    // OK
    

    end example]

  • В противном случае, если список инициализаторов имеет единственный элемент типа E и либо T не является ссылочным типом, либо его ссылочный тип связан со ссылкой E, объект или ссылка инициализируются из этого элемента (путем инициализации копирования для инициализации списка копирования, или путем прямой инициализации для инициализации прямым списком); если для преобразования элемента требуется сужающее преобразование (см. ниже) T, программа имеет неправильный формат.

    [Example:

    int x1 {2};                         // OK
    int x2 {2.0};                       // error: narrowing
    

    end example]

  • В противном случае, если T это ссылочный тип, создается prvalue типа, на который ссылается T . Prvalue инициализирует свой объект результата путем инициализации списка-копирования или инициализации прямым списком, в зависимости от типа инициализации для ссылки. Затем prvalue используется для прямой инициализации ссылки. [ Note: Как обычно, привязка завершится неудачно, и программа будет иметь неправильный формат, если ссылочный тип является ссылкой lvalue на неконстантный тип. ] end note

    [Example:

    struct S {
      S(std::initializer_list<double>); // #1
      S(const std::string&);            // #2
      // ...
    };
    const S& r1 = { 1, 2, 3.0 };        // OK: invoke #1
    const S& r2 { "Spinach" };          // OK: invoke #2
    S& r3 = { 1, 2, 3 };                // error: initializer is not an lvalue
    const int& i1 = { 1 };              // OK
    const int& i2 = { 1.1 };            // error: narrowing
    const int (&iar)[2] = { 1, 2 };     // OK: iar is bound to temporary array
    

    end example]

  • В противном случае, если список инициализаторов не имеет элементов, объект инициализируется значением.

    [Example:

    int** pp {};                        // initialized to null pointer
    

    end example]

  • В противном случае программа имеет неверный формат.

    [Example:

    struct A { int i; int j; };
    A a1 { 1, 2 };                      // aggregate initialization
    A a2 { 1.2 };                       // error: narrowing
    struct B {
      B(std::initializer_list<int>);
    };
    B b1 { 1, 2 };                      // creates initializer_­list<int> and calls constructor
    B b2 { 1, 2.0 };                    // error: narrowing
    struct C {
      C(int i, double j);
    };
    C c1 = { 1, 2.2 };                  // calls constructor with arguments (1, 2.2)
    C c2 = { 1.1, 2 };                  // error: narrowing
    
    int j { 1 };                        // initialize to 1
    int k { };                          // initialize to 0
    

    end example]

Внутри initializer-listэлемента a braced-init-list, initializer-clausesвключая все, что является результатом pack expansions, оцениваются в том порядке, в котором они появляются. То есть каждое вычисление значения и побочный эффект, связанный с данным initializer-clause, упорядочивается перед каждым вычислением значения и побочным эффектом, связанным с любым, initializer-clause который следует за ним в списке разделенных запятыми файлов initializer-list. [ Note: Этот порядок оценки сохраняется независимо от семантики инициализации; например, это применяется, когда элементы initializer-listинтерпретируются как аргументы вызова конструктора, даже если обычно нет ограничений последовательности для аргументов вызова. ] end note

Объект типа std​::​initializer_­list<E> создается из списка инициализаторов, как если бы реализация была сгенерирована, и materialized prvalue типа «массив из N const E», где N - количество элементов в списке инициализаторов. Каждый элемент этого массива инициализируется копией с соответствующим элементом списка инициализаторов, и std​::​initializer_­list<E> объект создается для ссылки на этот массив. [ Note: Конструктор или функция преобразования, выбранные для копии, должны находиться accessible в контексте списка инициализаторов. ] Если для инициализации любого из элементов требуется сужающее преобразование, программа имеет неправильный формат. [end noteExample:

struct X {
  X(std::initializer_list<double> v);
};
X x{ 1,2,3 };

Инициализация будет реализована примерно так:

const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));

предполагая, что реализация может построить initializer_­list объект с парой указателей. ] end example

Массив имеет такое же время жизни, как и любой другой temporary object, за исключением того, что инициализация initializer_­list объекта из массива продлевает время жизни массива точно так же, как привязка ссылки к временному объекту. [Example:

typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1, 2, 3 };

void f() {
  std::vector<cmplx> v2{ 1, 2, 3 };
  std::initializer_list<int> i3 = { 1, 2, 3 };
}

struct A {
  std::initializer_list<int> i4;
  A() : i4{ 1, 2, 3 } {}  // ill-formed, would create a dangling reference
};

Для v1 и v2, то initializer_­list объект является параметром при вызове функции, поэтому массив создан { 1, 2, 3 } имеет срок службы полного выражения. Для получения i3, то initializer_­list объект является переменной, так что сохраняется массив для времени жизни переменной. Для получения i4, то initializer_­list объект инициализируется в конструкторе это , ctor-initializerкак если бы за счет связывания временный массив опорного элемента, так что программа плохо сформированные ([class.base.init]). ] [ Реализация может разместить массив в постоянной памяти, если можно выделить явный массив с тем же инициализатором. ]end exampleNote: end note

A narrowing conversion - неявное преобразование

  • от типа с плавающей запятой к целочисленному типу, или

  • от long double до double или float, или от double до float, за исключением случаев, когда источник является постоянным выражением, а фактическое значение после преобразования находится в пределах диапазона значений, которые могут быть представлены (даже если оно не может быть представлено точно), или

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

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

[ Note: Как указано выше, такие преобразования не разрешены на верхнем уровне при инициализации списка. ] [end noteExample:

int x = 999;              // x is not a constant expression
const int y = 999;
const int z = 99;
char c1 = x;              // OK, though it might narrow (in this case, it does narrow)
char c2{x};               // error: might narrow
char c3{y};               // error: narrows (assuming char is 8 bits)
char c4{z};               // OK: no narrowing needed
unsigned char uc1 = {5};  // OK: no narrowing needed
unsigned char uc2 = {-1}; // error: narrows
unsigned int ui1 = {-1};  // error: narrows
signed int si1 =
  { (unsigned int)-1 };   // error: narrows
int ii = {2.0};           // error: narrows
float f1 { x };           // error: might narrow
float f2 { 7 };           // OK: 7 can be exactly represented as a float
int f(int);
int a[] =
  { 2, f(2), f(2.0) };    // OK: the double-to-int conversion is not at the top level

end example]