共计 6908 个字符,预计需要花费 18 分钟才能阅读完成。
初始化 (Initialization)
-
初始化的模式
- Direct-initialization
- Copy-initialization
- 初始化的含意
- Value initialize
- Default initialize
- Zero initialize
-
List initialize
- narrowing conversion
- 字符数组初始化
-
聚合(aggregate)初始化
- Aggregate
- 援用初始化
基于 c ++20 draft n4868
初始化的模式
通常讲到初始化,会想起变量的时候给的初始化值(initializer)。然而除此之后,还有一些中央会产生对象的初始化。所有的这些初始化操作听从同一套规定。
初始化依照其模式,能够分为两大类,direct-initialization 与 copy-initialization
Direct-initialization
-
变量定义中初始化值无
=
int i{1}; std::vector<std::string> vec(3);
-
new-initializer
int *p = new int(3);
,应用3
初始化一个新建的int
对象
-
static_cast
static_cast
会应用其参数初始化一个后果类型的对象,如:static_cast<int>(2.0);
,其中应用2.0
初始化一个int
对象。
-
函数调用像是的类型转换
int(2.0)
,其中应用2.0
初始化一个int
类型的对象
-
condition 中的 brace-init-list
if (Derived *pDerived{dynamic_cast<Derived*>(pBase)})
,应用dyanmic_cast<Drived*>(pBase)
初始化pDerived
Copy-initialization
-
变量定义中应用
=
模式初始化int i = 1;
,应用1
初始化i
-
condition 中应用
=
模式初始化if (Derived *pDerived = dynamic_cast<Derived*>(pBase))
,应用dyanmic_cast<Drived*>(pBase)
初始化pDerived
-
参数传递,函数返回值
-
示例:
int foo(int i) {return i+1;} void bar() {foo(1); }
应用
1
初始化foo
的参数i
,应用i+1
初始化函数的返回值(右值)
-
-
抛出异样,异样捕捉
-
示例:
void foo() {std::string s{123}; try {throw s;} cache (string &es) {}}
在
throw s;
中,应用s
初始化一个长期对象。在cache (string &es)
中,长期对象的左值用于初始化es
。这两个初始化都是 copy initialize
-
-
聚合成员初始化。在聚合初始化中,每个成员的初始化都是 copy initialization
std::string str[]{"abc", "def"}
,应用"abc"
初始化str[0]
,应用"def"
初始化str[1]
都是 copy-initialize。留神对str
的初始化应用的是 direct-initialize 的模式。
初始化的含意
依据被初始化对象的类型与初始化的模式不同,初始化会有不同的含意。具体初始化含意见下。copy-initialize 与 direct-initialize 均应用如下定义。
- 如果 initializer 是一个 braced-init-list 或者
=
branced-init-list,执行列表初始化 - 如果指标类型是援用类型,则执行援用初始化
- 如果指标类型是
char
,singed char
,unsigned char
,char8_t
,char16_t
,char32_t
,wchar_t
的数组,initializer 是一个 string-literal,执行字符数组初始化 - 如果 initializer 是
()
,执行 value-initialze - 如果指标类型是数组,initializer 应该是
(
pxpression-list)
,且其中的元素个数不应比数组长度更多。数组中的元素将顺次由 epression-list 中的值 copy-initialize。对没有提供值的元素(数组长度比 expression-list 长),进行 value-initialize -
如果指标类型是类类型(class type):
- 如果 initializer 是一个右值并且与指标类型雷同(疏忽 cv-qualifier),则 initializer 用于初始化指标对象
-
如果 1) 这是 direct initialization;或者 2) 这是 copy initialization 且源类型与指标类型雷同,或者是指标类型的派生类,那么应用构造函数初始化
- 如果能够找到惟一可用的构造函数,那么该构造函数用于初始化对象
- 如果没有可用的构造函数,且指标类型是一个聚合(aggregate class),intializer 为
(
expression-list)
,则应用 expression-list 中的值顺次 copy initialize 对应元素。未初始化值的元素执行 value initialize。 - 否则,程序非法
- 否则,尝试用户定义类型转换(包含构造函数)。不能找到适合的类型转换的程序非法。类型转换的后果将被用于 direct initialize 指标对象。
- 否则,如果源类型是一个类类型,将尝试用户定义类型转换。不能找到适合的类型转换的程序非法。
- 否则,如果这个是一个 direct initialization,原类型是
std::nullptr_t
,指标类型是bool
,则指标对象被初始化为false
- 否则,如果须要,能够应用一个规范转换序列 (stand conversion sequence) 将源对象转换为指标类型。无奈转换则程序非法。
Value initialize
对一个类型 value initialize 含意是:
-
如果其为类类型,则
- 如果该类没有默认构造函数,或者有用户提供的(user-provided)或 deleted 默认构造函数,该对象被 default initialize
-
否则,对象被 zero initialize;并且,如果该类有 non-trivial 默认构造函数,该对象被 value initialize。
-
例如
#include <string> struct A{ int a; std::string s; }; int main() {A a_obj{}; // a_obj.a == 0 }
类
A
有默认构造函数(主动生成的),该默认构造函数不是 user-provided,也不是 deleted,因此进入该条解决。a_obj
被 zero initialize。另外,因为其默认构造函数是 non-trivial(它含有个一个std::string
成员),因此被进一步默认结构。
因为有 zero initialize 存在,a_obj.a == 0
。
-
- 如果指标对象类型为数组,则数组的每一个元素被 value initialize
- 否则,对象被 zero initialize
Default initialize
对一个类型 default initialize 含意是:
- 对类类型,调用默认构造函数。
- 对数组类型,每一个元素被 default initialize
- 其余,不执行初始化操作
Zero initialize
对一个类型 default initialize 含意是:
- scalar type 初始化为 0
- 对非 union 类类型,其 padding 被初始化为 0,非静态数据成员,基类 被 zero initialize
- 对 union,padding 被初始化为 0,第一个非动态成员被 zero initialize
- 对数组,每一个元素被 zero initialize
- 对援用,不进行初始化
List initialize
- 如果 brace-init-list 蕴含 designated-initializer-list,指标类型必须为聚合(aggregate class),其中成员的呈现程序须要与定义程序统一。执行聚合初始化(aggregate initialize)
- 如果指标是聚合类(aggregate class),initializer list 仅蕴含一个元素,其类型与指标类型雷同或者是其派生类,那么应用该元素初始化指标对象。
- 如果指标类型是数组,initializer list 仅蕴含一个元素,且为适合类型的字符串字面量,则执行字符数组初始化。
- 如果指标类型是聚合类型(aggregate class),执行聚合初始化
- 如果 initializer list 为空,指标类型为有默认构造函数的类类型,指标对象被 value initialize
- 如果指标类型是
std::initializer_list<E>
:创立一个const E[N]
类型的数组,其中每一个元素用 initializer list 中的元素 copy initialize,指标对象将援用这个数组。(如果任何一个元素初始化是须要 narrowing conversion,则程序非法) - 如果指标类型是类类型,应用构造函数初始化。(如果参数初始化时须要 narrowing conversion,程序非法)
- 如果指标类型是有确定 underlying type 的枚举,intiiailizer list 仅含有一个元素,该元素能够被隐式转换为指标类型的 underlying type,并且该初始化是 direct initialization,则指标对象的值为源对象转换为指标类型的 underlying type 后的值。(不能够是 copy initialize;如果须要 narrawing conversion,程序非法)
- 如果 initialize list 有惟一元素,指标类型不为援用或者为一个 reference related to 源类型的援用,指标对象(或援用)由该元素初始化(如果须要 narrowing conversion,程序非法)
- 如果指标类型是援用,则用 initializer 执行 copy list initialize 初始化一个被援用类型的 prvalue,指标援用由该 prvalue direct initialize
- 如果 initializer list 为空,指标对象被 value initialize
- 其余,程序非法
narrowing conversion
narrowing conversion 包含:
- 从浮点类型到整形的转换
- 从
long double
到double
或float
,从double
到float
的转换,除非源值为常量表达式,且其在指标类型中可示意(包含可示意但无奈准确示意的状况) - 从整形或 unscoped enueration type 到浮点类型的转换,除非源值是一个常量表达式,且转换后果在指标类型可示意,且将转换后果再转换为源类型时其值不变
- 从整形或 unscoped enueration type 到整型的转换,该整型无奈示意源类型中的所有值,除非源值是一个常量表达式,其值在通过 integral promotion 之后在指标类型可示意
- 从指针或指向成员的指针类型到
bool
字符数组初始化
字符数组能够用字符串字面量初始化。字符数组的类型须要与字符串字面量类型匹配。字符数组的中的元素由字符串字面量中的对应元素进行初始化。
字符串字面量(包含最初的 '\0'
)不能比字符数组更长。如果字符串字面量较短,多余的数组元素将被 zero initialize。
聚合(aggregate)初始化
Aggregate
满足以下条件的数组或类(union 也是一品种)称作 aggregate:
- 没有申明构造函数,或继承(注:应用
using
)构造函数 - 没有公有或爱护的非静态数据成员
- 没有虚函数
- 没有虚的、公有的或被爱护的基类
aggregate 的成员指:
- 对于数组,指它的每一个元素
- 对于类,指按申明程序的每一个间接基类,以及按申明程序的每一个非静态数据成员(如果类中有匿名联结(union),匿名联结自身是 aggregate 的一个成员,但匿名联结的成员不是)
对于显式提供了值的成员,将应用该值进行 copy initialize。如果须要 narrowing conversion,程序非法。对于匿名联结(anonymous union),只能对其中一个成员提供初始化。
对于非 union 的 aggregate 中没有被显式初始化的成员:
- 如果成员申明了 default member initializer,应用该值初始化
- 如果该成员不是援用,应用
{}
copy initialize - 否则,程序非法
当一个 union aggregate 被应用 {}
初始化时:
- 如果任意成员(variant member)有 default member initializer,应用该值初始化这一成员
- 否则,union 的第一个成员应用
{}
初始化
aggregate 的成员如果也是 aggregate,子 aggregate 的初始化列表(在某些状况下)能够省略 {
, }
。空 {}
不能省略。如果省略,那么接下来的 initializer 全副被用于初始化子 aggregate,直到子 aggregate 所有成员都有了 initializer。残余的 initializer 用于初始化父 aggregate 的残余成员。
援用初始化
先要介绍两个概念:
- 类型 cv1
T1
与 cv2T2
是 reference related,示意T1
与T2
类似(约等于雷同),或T1
是T2
的基类。 - 类型 cv1
T1
与 cv2T2
是 reference compitable 示意指向 cv2T2
的指针能够通过一个规范转换序列(standard conversion sequence)转换为指向 cv1T1
的指针。(约等于 cv1 > cv2,并且T1
与T2
雷同,或者是T2
的(可拜访的且无歧义的)基类)
应用 cv2 T2
的值初始化 cv1 T1
的援用规定如下:
-
指标是左值援用并且满足以下条件之一:
- initialize 为左值,cv1
T1
与 cv2T2
是 reference compitable,援用绑定到该值(或其基类子对象) T2
是类,但T1
与T2
并非 reference related,但T2
能够转换为一个 cv3T3
的左值,且 cv1T1
与 cv3T3
是 reference compitable 的,援用绑定到转换后的T3
的左值(或其基类子对象)
- initialize 为左值,cv1
- 否则,如果指标为十分量左值援用,或者 volatile 左值援用,程序非法
-
否则:(以下为绑定至右值,prvalue 会被转换为 glvalue)
- 如果初始化值为一个右值(非位域),或一个函数,且 cv1
T1
与 cv2T2
是 reference compitable,援用绑定到该值(或其基类子对象) T2
是类,但T1
与T2
并非 reference related,但T2
能够转换为一个 cv3T3
的右值(或函数),且 cv1T1
与 cv3T3
是 reference compitable 的,援用绑定到转换后的T3
的 glvalue(或其基类子对象)
- 如果初始化值为一个右值(非位域),或一个函数,且 cv1
-
否则:
- 如果
T1
或T2
是类,且T1
与T2
并非 reference related,则尝试通过自定义转换应用初始化值初始化一个 cv1T1
的对象(如不能初始化相应的对象,则程序非法)。转换的后果将用于初始化指标援用。 -
其余状况,初始化值将被隐式转换为 cv1
T1
。援用绑定值转换的后果。- 如果
T1
与T2
是 reference related,那么 cv1 应大于 cv2,不能用 lvalue 初始化 rvalue reference。
- 如果
例如:
struct Banana { }; struct Enigma {operator const Banana(); }; struct Alaska {operator Banana&(); }; void enigmatic() { typedef const Banana ConstBanana; Banana &&banana1 = ConstBanana(); // error Banana &&banana2 = Enigma(); // error Banana &&banana3 = Alaska(); // error} 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
- 如果