369 lines
21 KiB
Markdown
369 lines
21 KiB
Markdown
[dcl.init.list]
|
||
|
||
# 9 Declarations [[dcl]](./#dcl)
|
||
|
||
## 9.5 Initializers [[dcl.init]](dcl.init#list)
|
||
|
||
### 9.5.5 List-initialization [dcl.init.list]
|
||
|
||
[1](#1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6366)
|
||
|
||
[*List-initialization*](#def:list-initialization "9.5.5 List-initialization [dcl.init.list]") is initialization of an object or reference from a[*braced-init-list*](dcl.init.general#nt:braced-init-list "9.5.1 General [dcl.init.general]")[.](#1.sentence-1)
|
||
|
||
Such an initializer is called an [*initializer list*](#def:initializer_list), and
|
||
the comma-separated[*initializer-clause*](dcl.init.general#nt:initializer-clause "9.5.1 General [dcl.init.general]")*s* of the [*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]") or[*designated-initializer-clause*](dcl.init.general#nt:designated-initializer-clause "9.5.1 General [dcl.init.general]")*s* of the [*designated-initializer-list*](dcl.init.general#nt:designated-initializer-list "9.5.1 General [dcl.init.general]") are called the [*elements*](#def:elements) of the initializer list[.](#1.sentence-2)
|
||
|
||
An initializer list may be empty[.](#1.sentence-3)
|
||
|
||
List-initialization can occur in direct-initialization or copy-initialization contexts;
|
||
list-initialization in a direct-initialization context is called[*direct-list-initialization*](#def:direct-list-initialization "9.5.5 List-initialization [dcl.init.list]") and list-initialization in a
|
||
copy-initialization context is called [*copy-list-initialization*](#def:copy-list-initialization "9.5.5 List-initialization [dcl.init.list]")[.](#1.sentence-4)
|
||
|
||
Direct-initialization that is not list-initialization is called[*direct-non-list-initialization*](#def:direct-non-list-initialization "9.5.5 List-initialization [dcl.init.list]")[.](#1.sentence-5)
|
||
|
||
[*Note [1](#note-1)*:
|
||
|
||
List-initialization can be used
|
||
|
||
- [(1.1)](#1.1)
|
||
|
||
as the initializer in a variable definition ([[dcl.init]](dcl.init "9.5 Initializers")),
|
||
|
||
- [(1.2)](#1.2)
|
||
|
||
as the initializer in a [*new-expression*](expr.new#nt:new-expression "7.6.2.8 New [expr.new]") ([[expr.new]](expr.new "7.6.2.8 New")),
|
||
|
||
- [(1.3)](#1.3)
|
||
|
||
in a return statement ([[stmt.return]](stmt.return "8.8.4 The return statement")),
|
||
|
||
- [(1.4)](#1.4)
|
||
|
||
as a [*for-range-initializer*](stmt.pre#nt:for-range-initializer "8.1 Preamble [stmt.pre]") ([[stmt.iter]](stmt.iter "8.6 Iteration statements")),
|
||
|
||
- [(1.5)](#1.5)
|
||
|
||
as a function argument ([[expr.call]](expr.call "7.6.1.3 Function call")),
|
||
|
||
- [(1.6)](#1.6)
|
||
|
||
as a template argument ([[temp.arg.nontype]](temp.arg.nontype "13.4.3 Constant template arguments")),
|
||
|
||
- [(1.7)](#1.7)
|
||
|
||
as a subscript ([[expr.sub]](expr.sub "7.6.1.2 Subscripting")),
|
||
|
||
- [(1.8)](#1.8)
|
||
|
||
as an argument to a constructor invocation ([[dcl.init]](dcl.init "9.5 Initializers"), [[expr.type.conv]](expr.type.conv "7.6.1.4 Explicit type conversion (functional notation)")),
|
||
|
||
- [(1.9)](#1.9)
|
||
|
||
as an initializer for a non-static data member ([[class.mem]](class.mem "11.4 Class members")),
|
||
|
||
- [(1.10)](#1.10)
|
||
|
||
in a [*mem-initializer*](class.base.init#nt:mem-initializer "11.9.3 Initializing bases and members [class.base.init]") ([[class.base.init]](class.base.init "11.9.3 Initializing bases and members")), or
|
||
|
||
- [(1.11)](#1.11)
|
||
|
||
on the right-hand side of an assignment ([[expr.assign]](expr.assign "7.6.19 Assignment and compound assignment operators"))[.](#1.sentence-6)
|
||
|
||
[*Example [1](#example-1)*: 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 elementsreturn { "Norah" }; // return list of one elementint* 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*]
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6413)
|
||
|
||
A constructor is an [*initializer-list constructor*](#def:initializer-list_constructor "9.5.5 List-initialization [dcl.init.list]") if its first parameter is
|
||
of type std::initializer_list<E> or reference tocv std::initializer_list<E> for some type E, and either there are no other
|
||
parameters or else all other parameters have default arguments ([[dcl.fct.default]](dcl.fct.default "9.3.4.7 Default arguments"))[.](#2.sentence-1)
|
||
|
||
[*Note [2](#note-2)*:
|
||
|
||
Initializer-list constructors are favored over other constructors in
|
||
list-initialization ([[over.match.list]](over.match.list "12.2.2.8 Initialization by list-initialization"))[.](#2.sentence-2)
|
||
|
||
Passing an initializer list as the argument
|
||
to the constructor template template<class T> C(T) of a class C does not
|
||
create an initializer-list constructor, because an initializer list argument causes the
|
||
corresponding parameter to be a non-deduced context ([[temp.deduct.call]](temp.deduct.call "13.10.3.2 Deducing template arguments from a function call"))[.](#2.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
The templatestd::initializer_list is not predefined;
|
||
if a standard library declaration ([[initializer.list.syn]](initializer.list.syn "17.11.2 Header <initializer_list> synopsis"), [[std.modules]](std.modules "16.4.2.4 Modules"))
|
||
of std::initializer_list is not reachable from ([[module.reach]](module.reach "10.7 Reachability"))
|
||
a use of std::initializer_list â
|
||
even an implicit use in which the type is not named ([[dcl.spec.auto]](dcl.spec.auto "9.2.9.7 Placeholder type specifiers")) â
|
||
the program is ill-formed[.](#2.sentence-4)
|
||
|
||
[3](#3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6433)
|
||
|
||
List-initialization of an object or reference of type *cv* T is defined as follows:
|
||
|
||
- [(3.1)](#3.1)
|
||
|
||
If the [*braced-init-list*](dcl.init.general#nt:braced-init-list "9.5.1 General [dcl.init.general]") contains a [*designated-initializer-list*](dcl.init.general#nt:designated-initializer-list "9.5.1 General [dcl.init.general]") andT is not a reference type,T shall be an aggregate class[.](#3.1.sentence-1)
|
||
The ordered [*identifier*](lex.name#nt:identifier "5.11 Identifiers [lex.name]")*s* in the [*designator*](dcl.init.general#nt:designator "9.5.1 General [dcl.init.general]")*s* of the [*designated-initializer-list*](dcl.init.general#nt:designated-initializer-list "9.5.1 General [dcl.init.general]") shall form a subsequence
|
||
of the ordered [*identifier*](lex.name#nt:identifier "5.11 Identifiers [lex.name]")*s* in the direct non-static data members of T[.](#3.1.sentence-2)
|
||
Aggregate initialization is performed ([[dcl.init.aggr]](dcl.init.aggr "9.5.2 Aggregates"))[.](#3.1.sentence-3)
|
||
[*Example [2](#example-2)*: struct A { int x; int y; int z; };
|
||
A a{.y = 2, .x = 1}; // error: designator order does not match declaration order A b{.x = 1, .z = 2}; // OK, b.y initialized to 0 â *end example*]
|
||
|
||
- [(3.2)](#3.2)
|
||
|
||
If T is an aggregate class and the initializer list has a single element
|
||
of type *cv1* U,
|
||
where U is T or a class derived from T,
|
||
the object is initialized from that element (by copy-initialization for
|
||
copy-list-initialization, or by direct-initialization for
|
||
direct-list-initialization)[.](#3.2.sentence-1)
|
||
|
||
- [(3.3)](#3.3)
|
||
|
||
Otherwise, if T is a character array and the initializer list has a
|
||
single element that is an appropriately-typed [*string-literal*](lex.string#nt:string-literal "5.13.5 String literals [lex.string]") ([[dcl.init.string]](dcl.init.string "9.5.3 Character arrays")),
|
||
initialization is performed as described in that subclause[.](#3.3.sentence-1)
|
||
|
||
- [(3.4)](#3.4)
|
||
|
||
Otherwise, if T is an aggregate, aggregate initialization is
|
||
performed ([[dcl.init.aggr]](dcl.init.aggr "9.5.2 Aggregates"))[.](#3.4.sentence-1)
|
||
[*Example [3](#example-3)*: double ad[] = { 1, 2.0 }; // OKint ai[] = { 1, 2.0 }; // error: narrowingstruct 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*]
|
||
|
||
- [(3.5)](#3.5)
|
||
|
||
Otherwise, if the initializer list has no elements and T is a class type with a
|
||
default constructor, the object is value-initialized[.](#3.5.sentence-1)
|
||
|
||
- [(3.6)](#3.6)
|
||
|
||
Otherwise, if T is a specialization of std::initializer_list,
|
||
the object is constructed as described below[.](#3.6.sentence-1)
|
||
|
||
- [(3.7)](#3.7)
|
||
|
||
Otherwise, if T is a class type, constructors are considered[.](#3.7.sentence-1)
|
||
The applicable constructors are enumerated and
|
||
the best one is chosen through overload resolution ([[over.match]](over.match "12.2 Overload resolution"), [[over.match.list]](over.match.list "12.2.2.8 Initialization by list-initialization"))[.](#3.7.sentence-2)
|
||
If a narrowing
|
||
conversion (see below) is required to convert any of the arguments, the program is
|
||
ill-formed[.](#3.7.sentence-3)
|
||
[*Example [4](#example-4)*: struct S { S(std::initializer_list<double>); // #1 S(std::initializer_list<int>); // #2 S(std::initializer_list<S>); // #3 S(); // #4// ...};
|
||
S s1 = { 1.0, 2.0, 3.0 }; // invoke #1 S s2 = { 1, 2, 3 }; // invoke #2 S s3{s2}; // invoke #3 (not the copy constructor) S s4 = { }; // invoke #4 â *end example*]
|
||
[*Example [5](#example-5)*: struct Map { Map(std::initializer_list<std::pair<std::string,int>>);};
|
||
Map ship = {{"Sophie",14}, {"Surprise",28}}; â *end example*]
|
||
[*Example [6](#example-6)*: 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*]
|
||
|
||
- [(3.8)](#3.8)
|
||
|
||
Otherwise, if T is an enumeration
|
||
with a fixed underlying type ([[dcl.enum]](dcl.enum "9.8.1 Enumeration declarations")) U,
|
||
the [*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]") has a single element v of scalar type,v can be implicitly converted to U, and
|
||
the initialization is direct-list-initialization,
|
||
the object is initialized with the value T(v) ([[expr.type.conv]](expr.type.conv "7.6.1.4 Explicit type conversion (functional notation)"));
|
||
if a narrowing conversion is required to convert v to U, the program is ill-formed[.](#3.8.sentence-1)
|
||
[*Example [7](#example-7)*: enum byte : unsigned char { };
|
||
byte b { 42 }; // OK byte c = { 42 }; // error byte d = byte{ 42 }; // OK; same value as b byte e { -1 }; // errorstruct A { byte b; };
|
||
A a1 = { { 42 } }; // error A a2 = { byte{ 42 } }; // OKvoid f(byte);
|
||
f({ 42 }); // errorenum class Handle : uint32_t { Invalid = 0 };
|
||
Handle h { 42 }; // OK â *end example*]
|
||
|
||
- [(3.9)](#3.9)
|
||
|
||
Otherwise, if
|
||
the initializer list
|
||
is not a [*designated-initializer-list*](dcl.init.general#nt:designated-initializer-list "9.5.1 General [dcl.init.general]") and
|
||
has a single element of type E and eitherT is not a reference type or its referenced type is
|
||
reference-related to E, the object or reference is initialized
|
||
from that element (by copy-initialization for copy-list-initialization,
|
||
or by direct-initialization for direct-list-initialization);
|
||
if a narrowing conversion (see below) is required
|
||
to convert the element to T, the program is ill-formed[.](#3.9.sentence-1)
|
||
[*Example [8](#example-8)*: int x1 {2}; // OKint x2 {2.0}; // error: narrowing â *end example*]
|
||
|
||
- [(3.10)](#3.10)
|
||
|
||
Otherwise, if T is a reference type, a prvalue is generated[.](#3.10.sentence-1)
|
||
The prvalue initializes its result object by copy-list-initialization from the initializer list[.](#3.10.sentence-2)
|
||
The prvalue is then used to direct-initialize the reference[.](#3.10.sentence-3)
|
||
The type of the prvalue is the type referenced by T,
|
||
unless T is âreference to array of unknown bound of Uâ,
|
||
in which case the type of the prvalue is
|
||
the type of x in the declaration U x[] H,
|
||
where H is the initializer list[.](#3.10.sentence-4)
|
||
[*Note [3](#note-3)*:
|
||
As usual, the binding will fail and the program is ill-formed if
|
||
the reference type is an lvalue reference to a non-const type[.](#3.10.sentence-5)
|
||
â *end note*]
|
||
[*Example [9](#example-9)*: struct S { S(std::initializer_list<double>); // #1 S(const std::string&); // #2// ...};const S& r1 = { 1, 2, 3.0 }; // OK, invoke #1const S& r2 { "Spinach" }; // OK, invoke #2 S& r3 = { 1, 2, 3 }; // error: initializer is not an lvalueconst int& i1 = { 1 }; // OKconst int& i2 = { 1.1 }; // error: narrowingconst int (&iar)[2] = { 1, 2 }; // OK, iar is bound to temporary arraystruct A { } a;struct B { explicit B(const A&); };const B& b2{a}; // error: cannot copy-list-initialize B temporary from Astruct C { int x; };
|
||
C&& c = { .x = 1 }; // OK â *end example*]
|
||
|
||
- [(3.11)](#3.11)
|
||
|
||
Otherwise, if the initializer list has no elements, the object is
|
||
value-initialized[.](#3.11.sentence-1)
|
||
[*Example [10](#example-10)*: int** pp {}; // initialized to null pointer â *end example*]
|
||
|
||
- [(3.12)](#3.12)
|
||
|
||
Otherwise, the program is ill-formed[.](#3.12.sentence-1)
|
||
[*Example [11](#example-11)*: struct A { int i; int j; };
|
||
A a1 { 1, 2 }; // aggregate initialization A a2 { 1.2 }; // error: narrowingstruct B { B(std::initializer_list<int>);};
|
||
B b1 { 1, 2 }; // creates initializer_list<int> and calls constructor B b2 { 1, 2.0 }; // error: narrowingstruct C { C(int i, double j);};
|
||
C c1 = { 1, 2.2 }; // calls constructor with arguments (1, 2.2) C c2 = { 1.1, 2 }; // error: narrowingint j { 1 }; // initialize to 1int k { }; // initialize to 0 â *end example*]
|
||
|
||
[4](#4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6651)
|
||
|
||
Within the [*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]") of a [*braced-init-list*](dcl.init.general#nt:braced-init-list "9.5.1 General [dcl.init.general]"),
|
||
the [*initializer-clause*](dcl.init.general#nt:initializer-clause "9.5.1 General [dcl.init.general]")*s*, including any that result from pack
|
||
expansions ([[temp.variadic]](temp.variadic "13.7.4 Variadic templates")), are evaluated in the order in which they
|
||
appear[.](#4.sentence-1)
|
||
|
||
That is, every value computation and side effect associated with a
|
||
given [*initializer-clause*](dcl.init.general#nt:initializer-clause "9.5.1 General [dcl.init.general]") is sequenced before every value
|
||
computation and side effect associated with any [*initializer-clause*](dcl.init.general#nt:initializer-clause "9.5.1 General [dcl.init.general]") that follows it in the comma-separated list of the [*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]")[.](#4.sentence-2)
|
||
|
||
[*Note [4](#note-4)*:
|
||
|
||
This evaluation ordering holds regardless of the semantics of the
|
||
initialization; for example, it applies when the elements of the[*initializer-list*](dcl.init.general#nt:initializer-list "9.5.1 General [dcl.init.general]") are interpreted as arguments of a constructor
|
||
call, even though ordinarily there are no sequencing constraints on the
|
||
arguments of a call[.](#4.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[5](#5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6667)
|
||
|
||
An object of type std::initializer_list<E> is constructed from
|
||
an initializer list as if
|
||
the implementation generated and materialized ([[conv.rval]](conv.rval "7.3.5 Temporary materialization conversion"))
|
||
a prvalue of type âarray of N const Eâ,
|
||
where N is the number of elements in the initializer list;
|
||
this is called the initializer list's [*backing array*](#def:array,backing "9.5.5 List-initialization [dcl.init.list]")[.](#5.sentence-1)
|
||
|
||
Each element of the backing array is copy-initialized with the
|
||
corresponding element of the initializer list, and thestd::initializer_list<E> object is constructed to refer to that array[.](#5.sentence-2)
|
||
|
||
[*Note [5](#note-5)*:
|
||
|
||
A constructor or conversion function selected for the copy needs to be
|
||
accessible ([[class.access]](class.access "11.8 Member access control")) in the context of the initializer list[.](#5.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
If a narrowing conversion is required to initialize any of the elements,
|
||
the program is ill-formed[.](#5.sentence-4)
|
||
|
||
[*Note [6](#note-6)*:
|
||
|
||
Backing arrays are potentially non-unique objects ([[intro.object]](intro.object "6.8.2 Object model"))[.](#5.sentence-5)
|
||
|
||
â *end note*]
|
||
|
||
[6](#6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6687)
|
||
|
||
The backing array has the same lifetime as any other temporary
|
||
object ([[class.temporary]](class.temporary "6.8.7 Temporary objects")), except that initializing aninitializer_list object from the array extends the lifetime of
|
||
the array exactly like binding a reference to a temporary[.](#6.sentence-1)
|
||
|
||
[*Example [12](#example-12)*: void f(std::initializer_list<double> il);void g(float x) { f({1, x, 3});}void h() { f({1, 2, 3});}struct A {mutable int i;};void q(std::initializer_list<A>);void r() { q({A{1}, A{2}, A{3}});}
|
||
|
||
The initialization will be implemented in a way roughly equivalent to this:void g(float x) {const double __a[3] = {double{1}, double{x}, double{3}}; // backing array f(std::initializer_list<double>(__a, __a+3));}void h() {static constexpr double __b[3] = {double{1}, double{2}, double{3}}; // backing array f(std::initializer_list<double>(__b, __b+3));}void r() {const A __c[3] = {A{1}, A{2}, A{3}}; // backing array q(std::initializer_list<A>(__c, __c+3));} assuming that the implementation
|
||
can construct an initializer_list object with a pair of pointers, and
|
||
with the understanding that __b does not outlive the call to f[.](#6.sentence-2)
|
||
|
||
â *end example*]
|
||
|
||
[*Example [13](#example-13)*: 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};
|
||
|
||
For v1 and v2, the initializer_list object
|
||
is a parameter in a function call, so the array created for{ 1, 2, 3 } has full-expression lifetime[.](#6.sentence-3)
|
||
|
||
For i3, the initializer_list object is a variable,
|
||
so the array persists for the lifetime of the variable[.](#6.sentence-4)
|
||
|
||
For i4, the initializer_list object is initialized in
|
||
the constructor's [*ctor-initializer*](class.base.init#nt:ctor-initializer "11.9.3 Initializing bases and members [class.base.init]") as if by binding
|
||
a temporary array to a reference member, so the program is
|
||
ill-formed ([[class.base.init]](class.base.init "11.9.3 Initializing bases and members"))[.](#6.sentence-5)
|
||
|
||
â *end example*]
|
||
|
||
[7](#7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L6758)
|
||
|
||
A [*narrowing conversion*](#def:conversion,narrowing "9.5.5 List-initialization [dcl.init.list]") is an implicit conversion
|
||
|
||
- [(7.1)](#7.1)
|
||
|
||
from a floating-point type to an integer type, or
|
||
|
||
- [(7.2)](#7.2)
|
||
|
||
from a floating-point type T to another floating-point type
|
||
whose floating-point conversion rank is neither greater than nor equal to
|
||
that of T,
|
||
except where the result of the conversion is a constant expression and
|
||
either its value is finite and the conversion did not overflow, or
|
||
the values before and after the conversion are not finite, or
|
||
|
||
- [(7.3)](#7.3)
|
||
|
||
from an integer type or unscoped enumeration type to a floating-point type, except
|
||
where the source is a constant expression and the actual value after conversion will fit
|
||
into the target type and will produce the original value when converted back to the
|
||
original type, or
|
||
|
||
- [(7.4)](#7.4)
|
||
|
||
from an integer type or unscoped enumeration type to an integer type that cannot
|
||
represent all the values of the original type, except where
|
||
* [(7.4.1)](#7.4.1)
|
||
|
||
the source is a bit-field whose width w is less than that of its type
|
||
(or, for an enumeration type, its underlying type) and
|
||
the target type can represent all the values
|
||
of a hypothetical extended integer type
|
||
with width w and with the same signedness as the original type or
|
||
|
||
* [(7.4.2)](#7.4.2)
|
||
|
||
the source is a constant
|
||
expression whose value after integral promotions will fit into the target type, or
|
||
|
||
- [(7.5)](#7.5)
|
||
|
||
from a pointer type or a pointer-to-member type to bool[.](#7.sentence-1)
|
||
|
||
[*Note [7](#note-7)*:
|
||
|
||
As indicated above, such conversions are not allowed at the top level in
|
||
list-initializations[.](#7.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[*Example [14](#example-14)*: int x = 999; // x is not a constant expressionconst int y = 999;const int z = 99;char c1 = x; // OK, though it potentially narrows (in this case, it does narrow)char c2{x}; // error: potentially narrowschar c3{y}; // error: narrows (assuming char is 8 bits)char c4{z}; // OK, no narrowing neededunsigned char uc1 = {5}; // OK, no narrowing neededunsigned char uc2 = {-1}; // error: narrowsunsigned int ui1 = {-1}; // error: narrowssigned int si1 ={ (unsigned int)-1 }; // error: narrowsint ii = {2.0}; // error: narrowsfloat f1 { x }; // error: potentially narrowsfloat f2 { 7 }; // OK, 7 can be exactly represented as a floatbool b = {"meow"}; // error: narrowsint f(int);int a[] = { 2, f(2), f(2.0) }; // OK, the double-to-int conversion is not at the top level â *end example*]
|