11 Declarators [dcl.decl]

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]