ISO/IEC 14882:2011之条款5.2——后缀表达式
5.2 后缀表达式
1、后缀表达式从左到右组合而成:
postfix-expression:
primary-expression
postfix-expression [ expression ]
postfix-expression [ braced-init-list ]
postfix-expression [ expression-listopt ]
simple-type-specifier ( expression-listopt )
typename-specifier ( expression-listopt )
simple-type-specifier braced-init-list
typename-specifier braced-init-list
postfix-expression . templateopt id-expression
postfix-expression -> templateopt id-expression
postfix-expression . templateopt pseudo-destructor-name
postfix-expression -> templateopt pseudo-destructor-name
postfix-expression ++
postfix-expression --
dynamic_cast < type-id > ( expression )
static_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
const_cast < type-id > ( expression )
typeid ( expression )
typeid ( type-id )
expression-list:
initializer-list
pseudo-destructor-name:
nested-name-specifieropt type-name :: ~ type-name
nested-name-specifieropt template simple-template-id :: ~ type-name
nested-name-specifieropt ~ type-name
~ decltype-specifier
2、在一个dynamic_cast、static_cast、reinterpret_cast或const_cast中跟在type-id后面的 > 符记可能是用两个连续的 > 符记来代替一个 >> 符记(14.2)。——注结束]
5.2.1 下标
1、在方括号中,后面跟着一个表达式的一个后缀表达式是一个后缀表达式。这些表达式的其中之一应该具有“指向T”的类型,而其它应该具有未绑定范围的枚举或是整数类型。该结果是类型“T”的一个左值。类型T应该是一个完整定义的对象类型。[注:这是真的,即使下标操作符被用在以下通常的习语中:&x[0]。——注结束]表达式E1[E2](由定义)与*((E1) + (E2))是相同的。[注:见5.3和5.7对 * 和 + 的详细描述以及8.3.4对数组的详细描述。——注结束]
2、一个braced-init-list不应该与内建的下标操作符一起使用。
5.2.2 函数调用
1、有两种类型的函数调用:普通函数调用和成员函数[注:一个静态成员函数(9.4)是一个普通函数](9.3)调用。一个函数调用是一个后缀表达式,后面跟着一对圆括号,里面可能包含一个可能是空,或是一组由逗号分隔的表达式列表,这些表达式构成了函数的实参。对于一个普通函数调用,后缀表达式应该要么是一个引用一个函数的左值(在这种情况下,函数到指针的标准转换(4.3)在后缀表达式上被抑制),要么它应该具有指向函数类型的指针。对于一个表达式,其函数类型具有与被调函数的定义的函数类型的语言连接所不同的语言连接,那么通过这个表达式来调用一个函数是未定义的(7.5)。对于一个成员函数调用,后缀表达式应该是一个隐式的(9.3.1,9.4)或是显式的类成员访问(5.2.5),其id-expression是一个函数成员名,或是选择一个函数成员的一个指向成员的表达式(5.5);该调用作为类对象的一个成员被对象表达式所引用。在一个隐式类成员访问的情况下,所隐含的对象被this所指着。[注:f()形式的一个成员函数调用被解释为(*this).f()(见9.3.1)——注结束]如果一个函数或成员函数名被使用,那么该名字可以被重载(条款13),在这种情况下应该根据13.3的规则来选择适当的函数。如果被选中的函数是非虚拟的,或如果在类成员访问表达式中的id-expression是一个qualified-id,那么那个函数被调用。[注:动态类型是由对象表达式的当前值所引用的对象的类型。12.7描述了当对象表达式在构造或析构下引用一个对象时,虚拟函数调用的行为。——注结束]
2、[注:如果一个函数或成员函数名被使用,并且名字查找(3.4)没有找到那个名字的一个声明,那么该程序是不良形式的。用这样的一个调用不能隐式声明任何一个函数。——注结束]
3、如果post-expression指明了一个析构器,那么函数调用表达式的类型是void;否则,函数调用表达式的类型是静态所选择的函数的返回类型(即忽略virtual关键字),即使实际被调用的函数的类型是不同的。该类型应该是一个对象类型、引用类型或void类型。
4、当一个函数被调用时,每个形参(8.3.5)应该用其相应的实参来初始化(8.5,12.8,12.1)。[注:这样的初始化以相互之间不确定的顺序进行的(1.9)——注结束]如果函数是一个非静态成员函数,那么该函数的this形参(9.3.2)应该用一个指向该调用的对象的指针来初始化,好比用一个显式类型转换(5.4)来转换。[注:对于该转换不会进行访问或歧义性检查;访问检查和歧义性消除是作为(可能隐式的)类成员访问操作符的一部分来完成。见10.2,11.2和5.2.5。——注结束]当一个函数被调用时,具有对象类型的形参应该具有完整定义的对象类型。[注:这仍然允许一个形参可以作为指向一个不完整类类型的一个指针或引用。然而,它防止了值传递形参具有一个不完整的类类型。——注结束]在一个形参的初始化期间,一个实现可以通过将对相关联的实参的转换绑定在一起以及/或用带有形参初始化的临时变量的构造(见12.2)来防止额外临时变量的构造。在一个函数定义其返回的地方一个形参的生命周期结束。每个形参的初始化和销毁在调用函数的上下文内发生。[例:对构造器、转换函数和析构器的访问在调用函数的调用点处被检查。如果一个函数形参的一个构造器或析构器抛出一个异常,那么一个异常处理的搜索在调用函数的作用域内开始;特别地,如果被调的函数具有一个带有一个可以处理此异常的异常处理的function-try-block(条款15),那么该处理不被考虑。——例结束]一个函数调用的值是被调函数所返回的值,除了在一个虚函数调用中,如果最终重写函数的返回类型与静态所选择的函数的返回类型不同,那么最终重写函数所返回的值被转换为静态所选择的函数的返回类型。
5、[注:一个函数可以改变其非const形参的值,但这些更改不会影响实参的值,除非一个形参是一个引用类型(8.3.2);如果该引用是对一个const限定类型的,需要使用const_cast来投射掉常量性以修改实参的值。一个临时变量在一个形参是const引用类型的地方被引入,如果需要的话(7.1.6,2.14,2.14.5,8.3.4,12.2)。此外,通过指针形参来修改非常量对象也是可能的。——注结束]
6、可以声明一个函数来接受比函数定义(8.4)的形参个数更少的实参(通过声明默认的实参(8.3.6))或更多的实参(通过使用省略号——...,或一个函数形参包(8.3.5)[译者注:对于函数形参包见14.5.3,可变参数模板])。[注:这暗示了除了一个省略号(...)或一个形参包被使用的地方之外,一个形参对每个实参可用。——注结束]
7、当对于一个给定的实参没有形参时,实参以这样的一种方式来传递——接收函数可以通过调用va_arg(18.10)来获得实参的值。本段落并不应用于传递给一个函数形参包的实参。函数形参包在模板实例化期间被展开,从而当一个函数模板特化被实际调用时,每个这样的实参具有一个相应的形参。——注结束]左值到右值(4.1)、数组到指针(4.2)以及函数到指针(4.3)的标准转换在实参表达式上被执行。具有(可能被cv限定)std::nullptr_t类型的实参被转换为类型void*(4.10)。在这些转换之后,如果实参并不具有算术、枚举、指针、指向成员的指针或类类型,那么程序是不良形式的。传递一个潜在被计算的,具有一个非平凡拷贝构造器,一个非平凡搬移构造器,而没有相应形参的类类型的实参,以实现定义的语义有条件地支持。如果实参具有从属于整型晋升(4.5)的整型或枚举类型,或是从属于浮点晋升(4.6)的一个浮点类型,那么实参的值在调用之前被转换为晋升后的类型。这些晋升被参照为默认实参晋升。
8、[注:后缀表达式与实参表达式的计算相对于每一个是不安排次序的。实参表达式计算的所有副作用在函数进入之前被顺序化(见1.9)。——注结束]
9、递归调用被允许,除了命名为main的函数(3.6.1)之外。
10、一个函数调用是一个左值,如果其结果类型是一个左值引用类型或是对一个函数类型的一个右值引用,如果其结果类型是一个对对象类型的一个右值引用,那么该函数调用是一个到期值,否则,它是一个纯右值。
11、如果一个函数调用是对象类型的一个纯右值:
——如果该函数调用要么是
——一个decltype-specifier的操作数,要么是
——一个逗号操作符的右操作数,它是一个decltype-specifier的操作数
为此纯右值不会引入一个临时对象。纯右值的类型应该是不完整的。[注:作为一个结果,不会为那个纯右值分配存储,并且它也不会被销毁;从而一个类类型在此上下文中作为成为一个函数调用的类型的一个结果不被实例化。这是真的,不管表达式是否使用了函数调用标记法或操作符标记法(13.3.1.2)。——注结束][注:不像对于一个decltype-specifier考虑一个id-expression是否被打上圆括号(7.1.6.2)的规则,圆括号在此上下文中不具有特殊意义。——注结束]
——否则,纯右值的类型应该是完整的。
5.2.3 显式的类型转换(函数标记法)
1、一个simple-type-specifier(7.1.6.2)或typename-specifier后面跟着加圆括号的expression-list构造了所给定的表达式列表的指定类型的一个值。如果表达式列表是一单个表达式,那么类型转换表达式与相应的投射表达式(5.4)等价(在已定义中且当被有意义地定义)。如果被指定的类型是一个类类型,该类类型应该是完整的。如果表达式列表指定了多个值,那么该类型应该是带有一个适当声明的构造器(8.5,12.1)的一个类,并且表达式T(x1, x2, ...)在效果上与声明T t(x1, x2, ...)等价;对于某些生成的临时变量t,结果是t的值作为一个纯右值。
2、表达式T(),T是一个非数组完整对象类型或(可能被cv限定的)void类型的一个simple-type-specifier或typename-specifier,创建了指定类型的一个纯右值,其值被初始化(8.5;对于void()情况不会做初始化)。当判定结果纯右值(3.10)的类型时,cv-qualifier被忽略。
3、类似地,后面跟着一个braced-init-list的一个simple-type-specifier或typename-specifier用指定的braced-init-list来创建指定类型直接列表初始化(8.5.4)的一个临时对象,并且其值是作为一个纯右值的那个临时对象。
5.2.4 伪析构器调用
1、在一个点 . 或箭头 -> 操作符之后使用一个pseudo-destructor-name表示由type-name或decltype-specifier所指明的非类类型的析构器。该结果应该只被用作为函数调用操作符 () 的操作数,并且这样一个调用的结果应该具有void类型。仅有的效果是在点或箭头之前的postfix-expression的计算。
2、点操作符的左手边应该是标量类型。箭头操作符的左手边应该是指向标量类型的指针。此标量类型是对象类型。对象类型以及由psuedo-destructor-name所指派的类型的cv非限定版本应该是同一个类型。此外,在以下形式的一个pseudo-destructor-name的两个type-name
nested-name-specifieropt type-name ::~ type-name
应该指派同一个标量类型。
5.2.5 类成员访问
1、一个后缀表达式后面跟着一个点 . 或一个箭头 -> ,可选地后面跟关键字template(14.2),然后后面跟着一个id-expression,是一个后缀表达式。在点或箭头之前的后缀表达式被计算[注:如果类成员访问表达式被计算,那么子表达式计算即使在结果不必要判定整个后缀表达式的值的情况下也会发生,比如,如果id-expression表示一个静态成员。];那个计算的结果与id-expression一起,确定了整个后缀表达式的结果。
2、对于第一种选择(点),第一个表达式应该具有完整的类类型。对于第二种选择(箭头),第一个表达式应该具有指向完整类类型的指针。表达式E1->E2被转换为(*(E1)).E2等价形式;5.2.5的其余部分将仅针对第一种选择(点)。[注:注意,(*(E1))是一个左值]在任一种情况下,id-expression将命名类或其其中一个基类的一个成员。[注:因为一个类的名字被插入在其类作用域(条款9)中,一个类的名字也被认为是那个类的一个嵌套成员。——注结束][注:3.4.5描述了名字是如何在 . 和 -> 操作符后被查找的。——注结束]
3、将postfix-expression . id-expression简写为E1.E2,E1被称为对象表达式。E1.E2的类型和值类别俺如下判定。在5.2.5的其余部分中,cq表示const或无const,而vq表示volatile或无volatile。cv表示cv限定符的任一集合,定义在3.9.3中。
4、如果E2被声明为具有“对T的引用”类型,那么E1.E2是一个左值;E1.E2的类型是T。否则,应用以下规则的其中之一:
——如果E2是一个静态数据成员而E2的类型是T,那么E1.E2是一个左值;此表达式指派了命名的类的成员。E1.E2的类型是T。
——如果E2是一个非静态数据成员,并且E1的类型是“cq1 vq1 X”,而E2的类型是“cq2 vq2 T”,那么该表达式指派第一个表达式所指派对象的命名的成员。如果E1是一个左值,那么E1.E2是一个左值;如果E1是一个到期值,那么E1.E2是一个到期值;否则,它是一个纯右值。设符号vq12表示vq1和vq2的“并”;即,如果cq1和cq2是const,那么cq12是const。如果E2被声明为一个mutable成员,那么E1.E2的类型是“vq12 T”。如果E2没有被声明为一个mutable成员,那么E1.E2的类型是“cq12 vq12 T”。
——如果E2是一个(可能被重载的)成员函数,那么函数重载决议(13.3)被用于判定E1.E2是引用一个静态的还是非静态的成员函数。
——如果它引用一个静态成员函数,并且E2的类型是“返回T的形参类型列表的函数”,那么E1.E2是一个左值;表达式指派了静态成员函数。E1.E2的类型与E2的类型是同一个,命名为“返回T的形参类型列表的函数”。
——否则,如果E1.E2引用一个非静态成员函数,并且E2的类型是“返回T的形参类型列表 cv ref-qualifieropt”,那么E1.E2是一个纯右值。该表达式指派了一个非静态成员函数。该表达式只可以被用作为一个成员函数调用(9.3)的左手边操作数。[注:任一组多余的围着该表达式的圆括号被忽略(5.1)。——注结束]E1.E2的类型是“返回T的形参类型列表 cv”。
——如果E2是一个嵌套的类型,那么表达式E1.E2是不良形式的。
——如果E2是一个成员枚举符,并且E2的类型是T,那么表达式E1.E2是一个纯右值。E1.E2的类型是T。
5、如果E2是一个非静态成员或一个非静态成员函数,那么该程序是不良形式的,如果E2直接是一个成员的类是E2的一个命名类(11.2)的一个有歧义的基(10.2)。[注:如果命名类是对象表达式的类类型的一个有歧义的基,那么程序也是不良形式的。
5.2.6 递增和递减
1、一个后缀 ++ 表达式的值是其操作数的值。[注:所获得的值是其原始值的一个拷贝。——注结束]操作数应该是一个可被修改的左值。操作数的类型应该是一个算术类型或是指向一个完整对象类型的一个指针。操作数对象的值通过对它加1进行修改,除非对象是bool类型,这种情况下,它被置为true。[注:这个用法被废弃了,见附录D。——注结束]对++表达式的值的计算在对操作数对象的修改之前被确定。对于一个不确定次序的函数调用,后缀++的操作仅仅是一次计算。[注:从而,一个函数调用不应该在左值到右值的转换和与任一单个后缀++操作符相关联的副作用之间进行干涉。——注结束]该结果是一个纯右值。结果的类型是操作数类型的cv非限定版本。见5.7和5.17。
2、后缀--的操作数是递减的,与后缀++操作符类似,除了操作数不应该是一个bool类型。[注:对于前缀递增和递减,见5.3.2。——注结束]
5.2.7 动态投射
1、表达式dynamic_cast<T>(v)的结果是将表达式v转换为类型T的结果。T应该是一个指向完整类类型的一个指针或引用,或是“指向cv void的指针”。dynamic_cast操作符不应该投射掉常量性(5.2.11)。
2、如果T是一个指针类型,那么v应该是一个指向完整类类型的一个指针的纯右值,并且该结果是类型T的一个纯右值。如果T是一个左值引用类型,那么v应该是一个完整类类型的一个左值,并且该结果是被T所引用的类型的一个左值。如果T是一个右值引用类型,那么v应该是具有一个完整类类型的一个表达式,并且该结果是被T所引用的类型的一个到期值。
3、如果v的类型与T的相同,或者它与T相同除了T中的类对象类型比v中的类对象类型更被cv限定,那么结果是v(如果有必要的话会被转换)。
4、如果v的值在指针情况下是一个空指针,那么结果是类型T的空指针值。
5、如果T是“指向cv1 B的指针”并且v具有类型“指向cv2 D的指针”,这里B是D的一个基类,那么结果是指向被v所指的D对象的唯一B子对象。类似地,如果T是“指向cv1 B的引用”,并且v具有类型cv2 D,这里B的D的一个基类,那么结果是被v所引用的D对象的唯一B子对象。[注:被v所指向或引用的最派生对象(1.8)可以包含其它B对象作为基类,但这些是被忽略的。]如果T是一个左值引用,那么结果是一个左值,或者如果T是一个右值引用,那么结果是一个到期值。在指针和引用两者情况下,如果cv2具有比cv1更大的cv限定,或者如果B是D的一个不可访问或有歧义的基类,那么程序是不良形式的。[例:
struct B { }; struct D : B{ }; void foo(D* dp) { B* bp = dynamic_cast<B*>(dp); // 等价于B* bp = dp; }
——例结束]
6、否则,v将是指向一个多态类型的指针或是一个多态类型的一个左值(10.3)。
7、如果T是指向“cv void的指针”,那么结果是指向被v所指的最派生对象的一个指针。否则,要应用一个运行时检查来看被v所指或引用的对象是否可以被转换为被T所指或引用的类型。
8、如果C是T所指或引用的类类型,那么运行时检查逻辑上执行以下操作:
——如果,在被v所指(引用)的最派生对象中,v指向(引用)一个C对象的一个public基类子对象,并且如果只有一个类型C的对象从被v指(引用)的子对象派生,那么结果指向(引用)那个C对象。
——否则,如果v指向(引用)最派生对象的一个public基类子对象,并且最派生对象的类型具有类型C的一个基类,C没有歧义,并且是公有继承的,那么结果指向(引用)最派生对象的C子对象。
——否则,运行时检查失败。
9、到指针类型的一个失败的投射的值是那个所需要的结果类型的空指针值。对引用类型的一个失败的投射抛出std::bad_cast(18.7.2)。
[例:
class A { virtual void f(); }; class B { virtual void g(); }; class D : public virtual A, private B { }; void g() { D d; B* bp = (B*)&d; // 需要投射来打破保护 A* ap = &d; // 公有派生,不需要投射 D& dr = dynamic_cast<D&>(*bp); // 失败 ap = dynamic_cast<A*>(bp); // 失败 bp = dynamic_cast<A*>(&d); // 失败 ap = dynamic_cast<A*>(&d); // 成功 bp = dynamic_cast<B*>(&d); // 不良形式的(不是一次运行时检查) } class E : public D, public B { }; class F : public E, public D { }; void h() { F f; A* ap = &f; // 成功:找到了唯一的A D* dp = dynamic_cast<D*>(ap); // 失败:产生0。f具有两个D子对象 E* ep = (E*)ap; // 不良形式的:从虚拟基类投射 E* ep1 = dynamic_cast<E*>(ap); }
——例结束][注:12.7描述了一个dynamic_cast在构造器或析构器下应用到一个对象的行为。——注结束]
5.2.8 类型标识
1、一个typeid表达式的结果是静态类型const std::type_info(18.7.1)和动态类型const std::type_info或const名字的一个左值,这里名字是一个由实现定义的公有派生自std::type_info的类,它保留着在18.7.1中所描述的行为。[注:对这样的一个类所推荐的名字是extended_type_info。]由该左值所引用的对象的生命周期延续到程序结束。在程序结束时对std::type_info对象是否调用析构器是未指定的。
2、当typeid被应用到一个泛化左值表达式,该表达式类型是一个多态类类型(10.3)中时,该结果引用一个std::type_info对象,改对象表示由泛化左值所引用的最派生对象(1.8)的类型(即动态类型)。如果泛化左值表达式通过将单目操作符 * 应用到一个指针来获得[注:如果p是指针类型的一个表达式,那么*p,(*p),*(p),((*p)),*((p)),等等均满足此要求],并且该指针是一个空指针值(4.10),那么typeid表达式抛出std::bad_typeid异常(18.7.3)。
3、当typeid被应用到一个表达式,不同于一个多态类类型的一个泛化左值时,该结果引用表示该表达式的静态类型的一个std::type_info对象。左值到右值(4.1),数组到指针(4.2),以及函数到指针(4.3)的转换不被应用到该表达式。如果表达式的类型是一个类类型,那么该类将是完整定义的。该表达式是一个不被计算的操作数(条款5)。
4、当typeid被应用到一个type-id时,结果引用表示type-id类型的一个std::type_info对象。如果type-id的类型是对一个可能的cv限定的一个引用,typeid表达式的结果引用表示cv非限定引用类型的一个std::type_info对象。如果type-id的类型是一个类类型或是对一个类类型的一个引用,那么该类将是完整定义的。
5、作为typeid操作数的泛化左值表达式或type-id的顶层cv限定符总被忽略。[例:
class D { /* ... */ }; D d1; const D d2; typeid(d1) == typeid(d2); // 产生true typeid(D) == typeid(const D); // 产生true typeid(D) == typeid(d2); // 产生true typeid(D) == typeid(const D&); // 产生true
——例结束]
6、如果头文件<typeinfo>(18.7.1)在使用typeid之前没有被包含,那么程序是不良形式的。
7、[注:12.7描述了在构造函数或析构函数下应用到一个对象的typeid的行为。——注结束]
5.2.9 静态投射
1、表达式static_cast<T>(v)的结果是将表达式v转换为类型T的的结果。如果T是一个左值引用类型或是对函数类型的一个右值引用,那么结果是一个左值;如果T是一个对对象类型的右值引用,那么结果是一个到期值;否则,结果是一个纯右值。static_cast操作符将不会投射掉常量性(5.2.11)。
2、类型“cv1 B”的一个左值,这里B是一个类类型,可以被投射到“对cv2 D的引用”类型,这里D是一个派生(条款10)自B的一个类,如果一个有效的从“指向D的指针”到“指向B的指针”的标准转换存在(4.10),那么cv2比cv1具有相同或更大的cv限定,并且B既不是一个D的虚基类也不是D的一个虚基类的一个基类。该结果具有类型“cv2 D”。类型“cv1 B”的一个到期值可以被投射到具有与“cv1 B”类型的一个左值相同限制的“对cv2 D右值引用”类型。如果“cv1 B”类型的对象实际上是类型D的一个对象的一个子对象,那么结果引用类型D的封闭对象。否则,投射结果是未定义的。[例:
struct B { }; struct D : public B { }; D d; B &br = d; static_cast<D&>(br); // 产生对原始d对象的左值
——例结束]
3、类型“cv1 T1”的一个泛化左值可以被投射到“对cv2 T2的右值引用”类型,如果“cv2 T2”是与“cv1 T1”引用兼容(8.5.3)的话。该结果引用此对象或是其指定的基类子对象。如果T2是一个不可访问的(条款11)或是有歧义的(10.2)T1的基类,那么需要这么一个投射的一个程序是不良形式的。
4、否则,一个表达式e可以使用static_cast<T>(e)形式的一个static_cast被显式地转换为一个类型T,如果声明T t(e);是良好形式的话,对于某些所产生的临时变量t(8.5)。这么一个显式转换的效果与执行声明和初始化然后使用临时变量作为转换结果的效果一样。表达式e被用作为一个泛化左值当且仅当初始化将它用作为一个泛化左值。
5、否则,static_cast应该执行以下所列出的一个转换。不应该显式地使用一个static_cast来执行其它转换。
6、任一表达式可以被显式地转换为cv void类型,在这种情况下它变成了一个被丢弃值的表达式(条款5)。[注:然而,如果该值是一个临时对象(12.2),那么为那个对象的析构器直到通常时间才被执行,并且对象的值出于执行析构器的目的而被保留。——注结束]。
7、对不包含一个左值到右值(4.1),数组到指针(4.2),函数到指针(4.3),空指针(4.10),空成员指针(4.11),或布尔(4.12)转换的任一标准转换顺序(条款4)的反转,可以用static_cast来显式执行。如果一个程序使用了static_cast来执行一个不良形式的标准转换序列,那么这个程序是不良形式的。[例:
struct B { }; struct D : private B { }; void f() { static_cast<D*>((B*)0); // 错误:B是D的一个私有基类 static_cast<int B::*>((int D::*)0); // 错误:B是D的一个私有基类 }
——例结束]
8、左值到右值(4.1),数组到指针(4.2),以及函数到指针(4.3)的转换被应用到操作数。这么一个static_cast受到限制——显式转换不会投射掉常量性(5.2.11),以及对特定情况遵循以下额外的规则:
9、一个枚举类型范围(7.2)内的一个值可以被显式地转为一个整型值。如果原始值可以用指定的类型来表示,那么该值不会被改变。否则,该结果值是未指定的。一个枚举类型范围内的一个值可以被显式地转为一个浮点类型;该结果与从原始值转换到浮点类型的一样。
10、整型或枚举类型的一个值可以被显式地转换为一个枚举类型。如果原始值在枚举值的范围内(7.2),那么该值不变。否则,该结果值是未指定的(并可能不在那个范围内)。浮点类型的一个值也可以被转换为一个枚举类型。该结果与从原始值转换为枚举的基础类型,然后再转为枚举类型的一样(4.9)。
11、“指向cv1 B的指针”类型的一个纯右值,这里B是一个类类型,可以被转换为“指向cv2 D的指针”类型的一个纯右值,这里D是一个派生(条款10)自B的一个类,如果一个有效的从“指向D的指针”到“指向B的指针”的标准转换存在(4.10),cv2比起cv1有相同或更高的cv限定,并且B既不是D的一个虚基类,也不是D的一个虚基类的一个一个基类。空指针值(4.10)被转换为目标类型的空指针值。如果“指向cv1 B的指针”类型的纯右值指向一个B,这个B实际上是类型D的一个对象的一个子对象,那么结果指针指向类型D的封闭对象。否则,投射的结果是未定义的。
12、“指向类型cv1 T的D的成员的指针”[译者注:即cv1 T D::*]类型的一个纯右值可以被转换为一个cv2 T类型的“指向B的成员的指针”类型的一个纯右值,这里B是D的一个基类(条款10),如果从“指向类型T的B的成员的指针”到“指向类型T的D的成员的指针”的一个有效标准转换存在(4.11),并且cv2比起cv1具有相同或更高的cv限定。[注:函数类型(包括那些用于指向成员函数类型的指针)永远不会被cv限定;见8.3.5。]空成员指针值(4.11)被转换为目标类型的空成员指针值。如果类B包含原始成员,或是一个包含那个原始成员的类的基类或派生类,那么指向成员的结果指针指向原始成员。否则,投射的结果是未定义的。[注:虽然类B不需要包含原始成员,但是该对象的动态类型必须包含原始成员,而在那个对象上,指向成员的指针被解引用;见5.5。——注结束]
13、“指向cv1 void指针”类型的一个纯右值可以被转换为“指向cv2 T的指针”类型的一个纯右值,这里,T是一个对象类型并且cv2比起cv1具有相同或更高的cv限定。空指针值被转换为目标类型的空指针值。指向对象类型的指针的一个值被转换为“指向cv void的指针”并在后面,可能带有不同不同cv限定,将具有其原始值。[例:
T* p1 = new T; const T* p2 = static_cast<const T*>(static_cast<void*>(p1)); bool b = p1 == p2; // b将具有值true
——例结束]
5.2.10 重新解释投射(Reinterpret cast)
1、表达式reinterpret_cast<T>(v)的结果是将表达式v转换为类型T的结果。如果T是一个左值引用类型或是对函数类型的一个右值引用,那么该结果是一个左值;如果T是一个对对象类型的右值引用,那么结果是一个到期值;否则,该结果是一个纯右值,并且左值到右值(4.1)、数组到指针(4.2)以及函数到指针(4.3)的标准转换被执行在表达式v上。可以显式地使用reinterpret_cast来执行的转换在以下列出。显式地使用reinterpret_cast不能执行其它转换。
2、reinterpret_cast操作符不应该投射掉常量性(5.2.11)。一个整型、枚举、指针,或指向成员的指针类型的表达式可以被显式地转换为其自己的类型;这样一种投射产生了其操作数的值。
3、[由reinterpret_cast所执行的映射,可能产生或可能不产生不同于原始值的一个表示。——注结束]
4、一个指针可以被显式地转换为足够能容下它大小的任一整数类型。映射函数是由实现定义的。[注:这对熟悉底层机器的编址结构的人来说不会感到惊讶。——注结束]类型std::nullptr_t的一个值可以被转换为一个整数类型;该转换与(void*)0到整数类型的转换具有相同意义和有效性。[注:一个reinterpret_cast不能被用于将任一类型的一个值转换到std::nullptr_t类型。——注结束]
5、整型类型或枚举类型的一个值可以被显式地转换为一个指针。被转换为充足尺寸的一个整型(如果有任一这么一种存在在实现中)以及回到相同指针类型的一个指针将具有其自己的原始值;在指针和整型之间的映射在其它情况下是由实现定义的。[注:除了在3.7.4.3中所描述的之外,这么一个转换的结果将不是一个安全派生的指针值。——注结束]
6、一个函数指针可以被显式地转换为一个不同类型的一个函数指针。通过指向一个函数类型(8.3.5)的一个指针调用一个函数,该函数类型与在函数定义中所使用的类型不同,这样的调用效果是未定义的。除了将“指向T1的指针”类型的一个纯右值转换为“指向T2的指针”类型(这里T1和T2是函数类型)并回到其原始类型产生原始指针值之外,这样的一个指针转换的结果是未指定的[注:也可以参考4.10获得指针转换更多细节。——注结束]
7、一个对象指针可以被显式地转换为一个不同类型的一个对象指针。[注:类型可以具有不同的cv限定符,不过一个reinterpret_cast不能投射掉常量性。]当“指向T1的指针”类型的一个纯右值被转换为“指向cv T2的指针”类型时,结果是static_cast<cv T2*>(static_cast<cv void*>(v)),如果T1与T2是标准布局的类型(3.9)并且T2的对齐要求不比T1的更严格,或者如果只要其中有一个类型是void。将“指向T1的指针”类型的一个纯右值转换为“指向T2的指针”类型(这里T1和T2是对象类型并且T2的对齐要求比T1更宽松)并回到其原始值产生原始指针值。任何其它这样的指针转换的结果是未指定的。
8、将一个函数指针转换为一个对象指针类型或将一个对象指针转换为一个函数指针是有条件被支持的。这样一个转换的意义是由实现定义的,除了如果一个实现支持在两个方向上的变换[译者注:即从函数指针到对象指针以及从对象指针到函数指针],将一种类型的一个纯右值转换为其它类型并回过来[译者注:其它类型转换为这种类型的一个纯右值],可能带有不同的cv限定,将产生原始指针值。
9、空指针值(4.10)被转换为目的类型的空指针值。[注:类型std::nullptr_t的一个空指针常量不能被转换为一个指针类型,并且一个整数类型的空指针常量不必要被转换为一个空指针值。——注结束]
10、“指向类型T1的X的成员的指针”类型的一个纯右值可以被显式地转换为“指向类型T2的Y的成员的指针”类型的一个不同类型的一个纯右值,如果T1和T2都是函数类型或都是对象类型。[注:T1和T2可以有不同的cv限定符,但受reinterpret_cast不能投射掉常量性的限制。]空成员指针值(4.11)被转换为目的类型的空成员指针值。这个转换的结果是未指定的,除了在以下情况下:
——将“指向成员函数的指针”类型的一个纯右值转换为一个不同的指向成员函数的指针类型并且回到其原始类型,产生指向成员值的原始指针。
——将“指向类型T1的X的数据成员的指针”类型的一个纯右值转换为“指向类型T2的Y的数据成员的指针”类型(这里,T2的对齐要求比T1的更宽松)并且回到其原始类型,产生指向成员值的原始指针。
11、类型T1的一个左值表达式可以被投射到"对T2引用"类型,如果“指向T1的指针”类型的一个表达式可以使用一个reinterpret_cast被显式地转换为“指向T2的指针”类型。即,一个引用投射reinterpret_cast<T&>(x)与带有内建的&和*操作符的转换*reinterpret_cast<T*>(&x)具有相同的效果(以及对于reinterpret_cast<T&&>(x)类似)。结果引用了与源左值同一个对象,但具有一个不同的类型。结果是对一个左值引用类型的一个左值,或对函数类型的一个右值引用,和对一个对象类型的右值引用的一个到期值。不会创建任一临时变量,也不会有拷贝,并且构造器(12.1)或转换函数(12.3)不会被调用。[注:这有时被称作为一个类型双关。]
5.2.11 常量投射
1、表达式const_cast<T>(v)的结果是类型T。如果T是一个对象类型的左值引用,那么结果是一个左值;如果T是一个对象类型的右值引用,那么结果是一个到期值;否则,结果是一个纯右值并且左值到右值(4.1),数组到指针(4.2),以及函数到指针(4.3)的标准转换在表达式v上执行。可以使用const_cast显式执行的转换在下面列出。其它转换都不应该使用const_cast进行显式执行。
2、[注:受本节中限制的影响,一个表达式可以使用一个const_cast操作符被投射到它自己的类型。——注结束]
3、对于两种指针类型,这里T1是指向cv1,0的指针,其指向cv1,1指针,...,指向cv1,n-1的指针,其指向cv1,n T的指针
并且T2是指向cv2,0的指针,其指向cv2,1指针,...,指向cv2,n-1的指针,其指向cv2,n T的指针。这里,T是任一对象类型或void类型,而cv1,k和cv2,k可能是不同的cv限定,类型T1的一个纯右值可以使用一个const_cast被显式地转换为类型T2。一个指针const_cast的结果引用源对象。
4、对于两个对象类型T1和T2,如果一个指向T1的指针可以使用一个const_cast被显式地转换为“指向T2的指针”类型,那么可以做下列转换:
——类型T1的一个左值可以使用投射const_cast<T2&>被显式地转换为类型T2的一个左值;
——类型T1的一个泛化左值可以使用投射const_cast<T2&&>被显式地转换为类型T2的一个到期值;
——如果T1是一个类类型,那么类型T1的一个纯右值可以使用投射const_cast<T2&&>被显式地转换为类型T2的一个到期值。
一个引用const_cast的结果引用源对象。
5、对于一个涉及指向数据成员的指针的const_cast,指向数据成员的多级指针以及多级混合指针以及指向数据成员的指针(4.4)对于const_cast的规则与那些用于指针的const_cast相同;当判定cv限定符在哪里被const_cast添加或移除时,一个指向成员的指针的“成员”方面被忽略。
6、一个空指针值(4.10)被转换为目标类型的空指针值。空成员指针值(4.11)被转换为目的类型的空成员指针值。
7、[注:依赖于对象类型,一个通过指针的写操作,左值或指向数据成员的指针,由一个投射掉一个const限定符[注:const_cast不限制于投射掉一个const限定符的转换]的const_cast所导致的,会产生未定义行为(7.1.6.1)。——注结束]
8、下列规则定义了被称为投射掉常量性的过程。在这些规则中Tn和Xn代表类型。对于两个指针类型:
X1是T1cv1, 1 * ... cv1,N * 这里T1不是一个指针类型
X2是T2cv2, 1 * ... cv2,M * 这里T2不是一个指针类型
K为min(N, M)
从X1到X2的投射投射掉了常量性,如果对于一个非指针类型T,则不存在一个从下列表达式的隐式转换:
Tcv1,(N-K+1) * cv1,(N-K+2) * ... cv1,N *
Tcv2,(M-K+1) * cv2,(M-K+2) * ... cv2,M *
9、使用一个左值引用投射从类型T1的一个左值到类型T2的一个左值的投射,或使用一个右值引用从类型T1的一个表达式投射到类型T2的一个到期值投射掉常量性,如果一个投射从“指向T1的指针”类型的一个纯右值投射到“指向T2指针”的类型投射掉常量性。
10、从“指向类型T1的X的数据成员的指针”类型的一个纯右值投射到“指向类型T2的Y的数据成员的指针”类型投射掉常量性,如果从“指向T1的指针”类型的一个纯右值投射到“指向T2的指针”类型投射掉常量性。
11、对于指向成员的多级指针和多级混合指针以及指向成员的指针(4.4),一个指向成员级的指针的“成员”方面被忽略,当判定一个const cv限定符是否已经被投射掉时。
12、【注:仅涉及到cv限定改变的某些转换不能使用const_cast来完成。比如,在指向函数的指针之间的转换不会被覆盖到,因为这种转换导致所使用的值引发未定义行为。对于某些理由,在指向成员函数的指针之间的转换,而且尤其是,从一个指向const成员函数的指针到一个指向非const成员函数的指针的转换不被覆盖到。——注结束】