ISO/IEC 14882:2011之条款4——标准转换
1、标准转换是带有内建意图的隐式转换。条款4枚举了这种转换的完整集合。一个标准转换序列是根据下列次序的标准转换:
——从以下集合的零个或一个转换:左值到右值的转换,数组到指针的转换,以及函数到指针的转换。
——从以下集合的零个或一个转换:整数晋升,浮点晋升,整数转换,浮点转换,浮点到整数转换,指针转换,指向成员的指针的转换,以及布尔转换。
——零个或一个限定转换。
[注:一个标准转换序列可以是一个空集,即它可以不包含任何协定。——注结束]
一个标准的转换序列将被应用到一个表达式,如果有必要将它转为一个需要的目标类型。
2、[注:带有一个所给类型的表达式在若干种上下文中将被隐式转为其它类型:
——当被用作为操作符的操作数。为其操作数的操作符的要求指定目标类型(条款5).
——当被用在一个if语句或迭代语句(6.4,6.5)条件中。目标类型是bool。
——当被用在一条switch语句中。目标类型为整型(6.4)。
——当被用作为一个初始化的源表达式时(该表达式包括了在一个函数调用中用作为一个实参以及用作为在一条return语句中的表达式)。正被初始化的实体的类型(通常)为目标类型。见8.5,8.5.3。——注结束]
3、一个表达式e可以被隐式地转换为一个类型T,当且仅当声明T t=e;是良好形式的,对于某些创建的临时变量t(8.5)。某些语言构建要求一个被转为一个布尔值。出现在这么一个上下文中的表达式e被称为结构上被转为一个布尔值并且是良好形式的,当且仅当声明bool t(e);是良好形式的,对于某些创建的临时变量t(8.5)。任一个隐式转换的效果与执行声明和初始化然后使用临时变量作为转换的结果相同。如果T是一个左值引用类型或是一个指向函数类型的右值引用(8.3.2),那么结果是一个左值;如果T是一个指向对象类型的右值引用,那么结果是一个到期值;在其它情况下为纯右值。表达式e被用作为一个泛化左值当且仅当该初始化将它用作为一个泛化左值。
4、[注:对于用户自定义类型,用户自定义的转换也被考虑;见12.3。通常而言,一个隐式转换序列(13.3.3.1)由一个标准转换序列后跟一个用户自定义转换再跟另一个标准转换序列组成。——注结束]
5、[注:某些转换在有些上下文中被禁止。比如,左值到右值的转换对于单目操作符&的操作数不被执行。特定的例外在那些操作符和上下文的描述中给出。——注结束]
4.1 左值到右值的转换
1、一个非函数、非数组类型T的一个泛化左值(3.10)可以被转换为一个纯右值。[注:由于历史原因,这个转换被称为“左值到右值”的转换,即使该名称并不精确地反映在3.10中所描述的表达式的分类系统。]如果T是一个不完整类型,那么使该转换成为有必要的一个程序是不良形式的。如果泛化左值所引用的对象并不是类型T的一个对象并且也不是从T所派生的一个类型的一个对象,或者如果该对象未被初始化,那么使该转换成为有必要的一个程序具有未定义行为。如果T是一个非类类型,那么纯右值的类型是T的cv非限定版本。否则,纯右值的类型是T。[注:在C++中,类纯右值可以具有cv限定类型(因为它们是对象)。这个与ISO C不同,ISO C中非左值永远不具有cv限定类型。]
2、当一个左值到右值的转换发生在一个未被计算的操作数或未被计算的操作数的子表达式中(条款5)时,那么包含在被引用的对象的值不被访问。否则,如果一个泛化左值具有一个类类型,那么该转换从泛化左值的类型T拷贝初始化一个临时变量并且转换的结果是该临时变量的纯右值。否则,如果泛化左值具有(可能被cv限定)类型std::nullptr_t,那么纯右值结果是一个空指针常量(4.10)。否则,包含在由泛化左值所指示的对象中的值是纯右值结果。
3、[注:见3.10。——注结束]
4.2 数组到指针的转换
1、“N个T类型的数组”类型或“未知边界的T类型的数组”类型的一个左值或右值可以被转换为“指向T的指针”类型的一个纯右值。该结果是一个指向该数组第一个元素的指针。
4.3 函数到指针的转换
1、函数类型T的一个左值可以被转换为“指向T的指针”类型的一个纯右值。该结果为指向函数的指针。[注:这个转换永远也不应用到非静态成员函数,因为引用一个非静态成员函数的一个左值无法被获得。]
2、[注:见13.4获得对于函数被重载情况的更多规则。——注结束]
4.4 限定转换
1、“指向cv1 T的指针”类型的一个纯右值可以被转为“指向cv2 T的指针”类型的一个纯右值,如果“cv2 T”比“cv1 T”更被cv限定。
2、“指向类型cv1 T类型的X的成员的指针”类型的一个纯右值可以被转为“指向类型cv2 T类型的X的成员的指针”类型的一个纯右值,如果“cv2 T”比“cv1 T”更被cv限定。
3、[注:函数类型(包括那些用在指向成员函数类型的指针)永远不被cv限定(8.3.5)。——注结束]
4、一个转换可以将cv限定符添加在多级指针的除第一层之外的其它层,受限于以下规则:[注:这些规则确保const安全性受转换保护。]
两个指针类型T1和T2是相似的,如果存在一个类型T并且整数n > 0,像这样:
T1是cv1,0的指针,指向cv1,1的指针,指向...cv1,n-1的指针,指向cv1,n T
并且
T2是cv2,0的指针,指向cv2,1的指针,指向...cv2,n-1的指针,指向cv2,n T
这里,每个cvi,j是const、volatile、const volatile或没有cv限定。在一个指针类型的第一个cv限定符之后的n元组cv限定符,比如,在T1指针类型中的cv1,1,cv1,2,...,cv1,n,被称为指针类型的cv限定签名。类型T1的一个表达式可以被转换为类型T2,当且仅当满足下列条件:
——指针类型是相似的。
——对于每个j > 0,如果const在cv1,j中,那么const也在cv2,j中;对于volatile也类似。
——如果cv1,j和cv2,j不同,那么对于0 < k < j,const在每个cv1,k中。
[注:如果一个程序可以将一个指向T**类型的一个指针赋值为一个指向const T**类型的指针(即,如果下面的行#1被允许),那么一个程序可能会不经意地修改一个const对象(就好比在行#2中所做的那样)。比如,
int main() { const char c = 'c'; char *pc; const char** ppc = &pc; // #1:不被允许 *ppc = &c; *pc = 'C'; // #2:修改了一个const对象 }
——注结束]
5、一个指向成员类型的多级指针,或是一个多级混合指针以及指向成员类型的指针具有以下形式:
cv0P0指向cv1P1指向...cvn-1Pn-1指向cvn T
这里,Pi要么是一个指针,要么是指向成员的指针,而T不是一个指针类型,也不是指向成员成员类型的指针。
6、两个指向成员类型的多级指针或两个多级混合指针以及指向成员类型的指针T1与T2是相似的,如果存在一个类型T并且整数n > 0,诸如:
T1是cv1,0P0指向cv1,1P1指向...cv1,n-1Pn-1指向cv1,n T
和
T2是cv2,0P0指向cv2,1P1指向...cv2,n-1Pn-1指向cv2,n T
7、对于类似的指向成员类型的多级指针以及类似的指向多级混合指针和指向成员类型的指针,添加cv限定符的规则与那些用于类似指针类型的cv限定符添加规则相同。
4.5 整数晋升
1、除了bool、char16_t、char32_t或wchar_t之外的整型类型的一个纯右值,其整型转换等级(4.13)比int的等级小,可以被转为int类型的一个纯右值,如果int可以表示该源类型的所有值;否则,源纯右值可以被转为unsigned int类型的一个纯右值。
2、char16_t、char32_t或wchar_t类型的一个纯右值可以被转为以下可以表示其基础类型的所有值的第一个纯右值:int,unsigned int,long int,unsigned long int,long long int,或unsigned long long int。如果在那个列表中没有一个类型可以用来表示其基础类型的所有值,那么类型char16_t、char32_t或wchar_t的一个纯右值可以被转为其基础类型的一个纯右值。
3、一个未设范围的枚举类型——其基础类型不固定——的一个纯右值可以被转换为可以表示枚举的所有值(即,在7.2中描述的bmin到bmax的范围内的值)的以下类型的第一个纯右值:int,unsigned int,long int,unsigned long int,long long int,或unsigned long long int。如果在那个列表中没有一个类型可以表示该枚举的所有值,一个未设范围的枚举类型的一个纯右值可以被转换为带有比long long int更高的最低整型转换等级(4.13)的扩展整数类型的一个纯右值,而枚举的所有值都可以在long long内被表示。如果有两个这样的扩展类型,那么选择带符号的那个。
4、一个未设范围的枚举类型——其基础类型不固定——的一个纯右值可以被转换为其基础类型的一个纯右值。此外,如果整数晋升可以被应用到其基础类型,那么一个未设范围的枚举类型——其基础类型不固定——的一个纯右值也可以被转换为被晋升的基础类型的一个纯右值。
5、一个整型位域(9.6)的一个纯右值可以被转为类型int的一个纯右值,如果int可以表示位域的所有的值;否则,它可以被转为unsigned int,如果unsigned int可以表示该位域的所有的值。如果该位域仍然更大,那么就没有整型晋升应用到它。如果该位域具有一个枚举类型,那么它被看作为用于晋升目的的那个枚举类型的任何其它值。
6、类型bool的一个纯右值可以被转换为类型int的一个纯右值,false变为零,而true变为一。
7、这些转换被称为整数晋升。
4.6 浮点晋升
1、float类型的一个纯右值可以被转为double类型的一个纯右值。该值保持不变。
2、这个转换被称为浮点晋升。
4.7 整数转换
1、一个整数类型的一个纯右值可以被转为另一个整数类型的一个纯右值。一个未绑定边界的枚举类型的一个纯右值可以被转换为一个整数类型的一个纯右值。
2、如果目的类型是无符号的,那么结果值是与源整数同余的最小无符号整数(模2n,n是用于表示无符号类型的比特个数)。[注:在一个二的补的表示中,这个转换是概念上的并且在位模式上没有改变(如果没有截断)。——注结束]
3、如果目标类型是带符号的,那么值不变,如果它可以在目标类型(以及位域宽度)中被表示;否则,该值是实现定义的。
4、如果目标类型是bool,见4.12。如果源类型是bool,那么值false被转为零,而值true被转为1。
5、这些转换被允许,因为整数晋升从整数转换的集合中被排除。
4.8 浮点转换
1、浮点类型的一个纯右值可以被转换为另一个浮点类型的一个纯右值。如果源值可以被完全以目标类型来表示,那么转换结果就是该完全的表示。如果源值在两个临近的目标值之间,那么转换的结果由实现定义来选择哪一个。否则,该行为是未定义的。
2、作为浮点晋升所允许的转换从浮点转换的集合中被排除。
4.9 浮点与整数的转换
1、一个浮点类型的一个纯右值可以被转换为一个整数类型的一个纯右值。该转换被截断;即,尾数(小数)部分被丢弃。如果被截断后的值不能表示目标类型,那么该行为是未定义的。[注:如果目标类型是bool,见4.12。——注结束]
2、一个整数类型的一个纯右值或一个未绑定边界的枚举类型的一个纯右值可以被转换为一个浮点类型的纯右值。转换的结果是完全的,如果可能的话。如果正被转换的值在可以被表示的值的范围内但该值不能被完全地表示出来,那么是由实现定义来选择取可表示的值的上界还是下界。[注:如果整数值不能作为浮点类型的一个值被完全表示,那么会发生精度丢失。——注结束]如果正被转换的值在可以被表示的值的范围内,那么行为是未定义的。如果源类型是bool,那么值false被转为零,而值true被转为一。
4.10 指针转换
1、一个空指针常量是一个整数类型的整数常量表达式(5.19)纯右值,计算值为零,或类型std::nullptr_t的一个纯右值。一个空指针常量可以被转为一个指针类型;结果是那个类型的空指针值并且与其它每种对象指针或函数指针类型的值有所区别。这样的一个转换称为一个空指针转换。相同类型的两个空指针值相比较应该相等。一个空指针常量到一个指向cv限定类型的一个指针的转换是一单个转换,而不是后面跟一个限定转换(4.4)的一个指针转换的序列。一个整数类型的空指针常量可以被转换为std::nullptr_t类型的一个纯右值。[注:结果纯右值并不是一个空指针值。——注结束]
2、“指向cv T的指针”类型的一个纯右值,这里T是一个对象类型,可以被转为“指向cv void的指针”的一个纯右值。将“指向cv T的指针”转为“指向cv void的指针”的结果指向类型T的对象所驻留的存储位置的起始处,就好比该对象是一个类型T的最派生对象(1.8)(即,不是一个基类子对象)。该空指针值被转为目标类型的空指针值。
3、“指向cv D的指针”类型的一个纯右值,这里D是一个类类型,可以被转为“指向cv B的指针”类型的一个纯右值,这里B是D的一个基类(条款10)。如果B是一个不可被访问的(条款11)或有歧义的(10.2)D的基类,那么需要此转换的一个程序是不良形式的。转换的结果是被派生类对象的基类子对象的一个指针。该空指针值被转为目标类型的空指针值。
4.11 指向成员的指针转换
1、一个空指针常量可以被转换为一个指向成员类型的一个指针;结果是那个类型的空成员指针值并且与任何不从一个空指针常量所创建的指向成员的指针有区别。这样的一个转换称为一个空成员指针转换。相同类型的两个空成员指针值比较应该相等。一个空指针常量到一个指向cv限定类型的成员指针的转换是一单个转换,而不是一个指向成员的指针的转换后跟一个限定转换的序列(4.4)。
2、“指向cv T类型的B的成员的指针”类型的一个纯右值,这里B是一个类类型,可以被转为“指向cv T类型的D的成员的指针”,这里D是B的一个派生类(条款10)。如果B是一个不可访问的(条款11),有歧义的(10.2),或D的虚拟(10.1)基类,或是D的一个虚基类的一个基类,那么需要此转换的一个程序是不良形式的。此转换的结果引用与在转换发生之前指向成员的指针相同的成员,但它引用基类成员,就好比它是那个派生类的一个成员。结果引用D的B的实例中的成员。由于结果具有“指向类型cv T的D的成员的指针”类型,因而它可以用一个D对象来解引用。结果是相同的,就好比指向B的成员的指针用D的B子对象来解引用。空成员指针值被转为目标类型的空成员指针值。[注:指向成员的指针的转换规则(从指向基的成员的指针到指向派生的成员的指针)与指向对象的指针的规则呈现相反(从指向派生的指针到指向基的指针)(4.10,条款10)。这个规则倒转是有必要的以确保类型安全。注意一个指向成员的指针不是一个对象的指针也不是一个函数指针并且这些指针的转换规则不应用到指向成员的指针。特别地,一个指向成员的指针不能被转为void*。]
4.12 布尔转换
1、算术、未绑定边界的枚举、指针、或指向成员类型的指针的一个纯右值可以被转换为bool类型的一个纯右值。一个零值、空指针值或空成员指针值被转为false;任何其它值被转为true。std::nullptr_t类型的一个纯右值可以被转为bool类型的一个纯右值;结果值为false。
4.13 整型转换等级
1、每个整数类型具有如下定义的一个整数转换等级:
——除了char和signed char之外(如果char是带符号的),没有哪两个带符号的整数类型应该具有相同的等级,即使它们具有相同的表示。
——一个带符号的整数类型的等级应该比任一具有一个更小大小的带符号整数类型更大。
——long long int的等级应该比long int的等级更大,而long int的等级应该比int的等级更大,int的等级应该比short int更大,short int的等级应该比signed char更大。
——任一无符号整数类型的等级应该与相应的带符号整数类型的等级相等。
——任一标准整数类型的等级应该比任一具有相同大小的扩展整数类型的等级更大。
——char的等级应该与signed char和unsigned char的等级相等。
——bool的等级应该比所有其它标准整数类型的等级更小。
——char16_t、char32_t以及wchar_t应该等于其基础类型的等级(3.9.1)。
——任一扩展带符号类型的等级与另一个具有相同大小的扩展带符号整数类型的关系是由实现定义的,但仍然遵循判定整数转换等级的规则。
——对于所有整数类型T1、T2、以及T3,如果T1具有比T2更大的等级,而T2具有比T3更大的等级,那么T1应该具有比T3更大的等级。
[注:整数转换等级被用在整数晋升(4.5)以及通常的算术转换(条款5)的定义中。——注结束]