buc*_*buc 79 c++ language-lawyer
考虑以下简短的C++程序:
#include <iostream>
class B {
public:
operator bool() const {
return false;
}
};
class B2 : public B {
public:
operator int() {
return 5;
}
};
int main() {
B2 b;
std::cout << std::boolalpha << (bool)b << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
如果我在不同的编译器上编译它,我会得到各种结果.使用Clang 3.4和GCC 4.4.7打印true,而Visual Studio 2013打印false,这意味着它们调用不同的强制转换操作符(bool)b.根据标准,这是正确的行为?
在我的理解operator bool()需要转变,而operator int()将需要int向bool转换,所以编译器应该选择第一个.是否const做一些与该是由编译器认为更"贵"常量转换?
如果我删除了const,所有编译器同样产生false输出.另一方面,如果我将两个类组合在一起(两个运算符将在同一个类中),则所有三个编译器都将生成true输出.
Rob*_*ahy 51
标准规定:
派生类中的转换函数不会隐藏基类中的转换函数,除非这两个函数转换为相同的类型.
§12.3[class.conv]
这意味着operator bool没有被隐藏operator int.
标准规定:
在重载解析期间,隐含的对象参数与其他参数无法区分.
§13.3.3.1[over.match.funcs]
在这种情况下b,"隐含的对象参数" 是类型的B2 &. operator bool需要const B2 &,所以编译器必须添加const b来调用operator bool.这 - 所有其他条件相同 - 可以operator int更好地匹配.
该标准规定a static_cast(在这种情况下,C风格的演员阵容正在执行)可以转换为类型T(在这种情况下int),如果:
T t(e);对于一些发明的临时变量,声明是格式良好的t.§5.2.9[expr.static.cast]
因此int可以转换为a bool,并且a bool可以同样地转换为a bool.
标准规定:
S考虑转换函数及其基类.那些未隐藏在其中的非显式转换函数S和yield类型T或可T通过标准转换序列转换为类型的类型是候选函数.§13.3.1.5[over.match.conv]
所以重载集由operator int和组成operator bool.所有其他条件相同,operator int是一个更好的匹配(因为你不必添加常量).因此operator int应该选择.
注意(可能违反直觉)标准一旦将它们添加到重载集(如上所述),就不会考虑返回类型(即这些运算符转换的类型),前提是其中一个参数的转换序列它们优于另一个参数的转换序列(由于常量,在这种情况下是这种情况).
标准规定:
给定这些定义,可行函数F1被定义为比另一个可行函数F2更好的函数,如果对于所有自变量i,ICSi(F1)不是比ICSi(F2)更差的转换序列,然后
- 对于某些参数j,ICSj(F1)是比ICSj(F2)更好的转换序列,或者,如果不是,
- 上下文是通过用户定义的转换初始化,从返回类型F1到目标类型的标准转换序列(即,正在初始化的实体的类型)是比返回类型的标准转换序列更好的转换序列F2到目的地类型.
§13.3.3[over.match.best]
在这种情况下,只有一个参数(隐含this参数).B2 &=> B2 &(调用operator int)的转换序列优于B2 &=> const B2 &(调用operator bool),因此operator int从重载集中选择,而不考虑它实际上不直接转换为bool.
转换函数operator int()是通过clang选择的,operator bool() const因为b它不是const限定的,而bool的转换运算符是.
短的理由是,对于重载解析候选的功能(在适当位置隐式对象参数),在转换时b向bool是
operator bool (B2 const &);
operator int (B2 &);
Run Code Online (Sandbox Code Playgroud)
其中第二个是更好的匹配,因为b不是const限定.
如果两个函数共享相同的限定条件(两者都兼有const),operator bool则选择它,因为它提供了直接转换.
如果我们同意调用布尔ostream插入器(std :: basic_ostream :: operator <<(bool val)作为[ostream.inserters.arithmetic])并使用转换为的结果b,bool我们可以深入研究转换.
b的演员布尔
(bool)b
Run Code Online (Sandbox Code Playgroud)
评估为
static_cast<bool>(b)
Run Code Online (Sandbox Code Playgroud)
根据C++ 11,5.4/4 [expr.cast]因为const_cast不适用(不在这里添加或删除const).
如果发明变量t形成良好,则每个C++ 11,5.2.9/4 [expr.static.cast]允许这种静态转换bool t(b);.根据C++ 11,8.5/15 [dcl.init],这些语句称为直接初始化.
bool t(b);最不提及的标准段落第16条规定(强调我的):
初始化器的语义如下.目标类型是要初始化的对象或引用的类型,源类型是初始化表达式的类型.
[...]
[...]如果源类型是(可能是cv限定的)类类型,则考虑转换函数.
枚举适用的转换函数,并通过重载决策选择最佳函数.
可用的转换函数operator int ()和operator bool() const自如C++ 11,12.3/5 [class.conv]告诉我们:
派生类中的转换函数不会隐藏基类中的转换函数,除非这两个函数转换为相同的类型.
而C++ 11,13.3.1.5/1 [over.match.conv]指出:
考虑S及其基类的转换函数.
其中S是将要转换的类.
C++ 11,13.3.1.5/1 [over.match.conv](强调我的):
1 [...]假设"cv1 T"是要初始化的对象的类型,并且"cv S"是初始化表达式的类型,S是类类型,候选函数选择如下:转换考虑S及其基类的函数.那些未隐藏在S和yield类型T中的非显式转换函数或可通过标准转换序列转换为类型T的类型是候选函数.
因此operator bool () const它是适用的,因为它不是隐藏在内B2并产生一个bool.
在最后一个标准引用中强调的部分与转换相关,operator int ()因为int是一种可以通过标准转换序列转换为bool的类型.转换为intto bool甚至不是一个序列,而是一个普通的直接转换,允许每个C++ 11,4.12/1 [conv.bool]
算术,无范围枚举,指针或指向成员类型的指针的prvalue可以转换为bool类型的prvalue.零值,空指针值或空成员指针值转换为false; 任何其他值都转换为true.
这意味着它operator int ()也适用.
通过重载决策(C++ 11,13.3.1.5/1 [over.match.conv])选择适当的转换函数:
重载分辨率用于选择要调用的转换函数.
当涉及类成员函数的重载解析时,有一个特殊的"怪癖":隐式对象参数".
Per C++ 11,13.3.1 [over.match.funcs],
[...]静态和非静态成员函数都有一个隐式对象参数[...]
其中非静态成员函数的此参数的类型 - 根据第4节:
对于没有ref-qualifier或使用&ref-qualifier声明的函数,"对cv X的左值引用"
对于使用&& ref-qualifier声明的函数的"对cv X的rvalue引用"
其中X是函数所属的类,cv是成员函数声明的cv-qualification.
这意味着(根据C++ 11,13.3.1.5 /2 [over.match.conv]),在转换函数的初始化中,
[t]他的参数列表有一个参数,它是初始化表达式.[注意:此参数将与转换函数的隐式对象参数进行比较. - 尾注]
重载解析的候选函数是:
operator bool (B2 const &);
operator int (B2 &);
Run Code Online (Sandbox Code Playgroud)
显然,operator int ()是如果使用类型的非恒定对象所请求的转换更好的匹配B2,因为operator bool ()所需要的资质转换.
如果两个转换函数共享相同的const限定,那么这些函数的重载解析将不再起作用.在这种情况下,转换(序列)排名到位.
operator bool ()当两个转换函数共享相同的const限定时,为什么选择?转换B2为bool是用户定义的转换序列(C++ 11,13.3.3.1.2/1 [over.ics.user])
用户定义的转换序列包括初始标准转换序列,然后是用户定义的转换,后跟第二个标准转换序列.
[...]如果转换函数指定了用户定义的转换,则初始标准转换序列会将源类型转换为转换函数的隐式对象参数.
C++ 11,13.3.3.2/3 [over.ics.rank]
[...]基于关系更好的转换序列和更好的转换来定义隐式转换序列的部分排序.
[...]用户定义的转换序列U1是比另一个用户定义的转换序列U2更好的转换序列,如果它们包含相同的用户定义的转换函数或构造函数或聚合初始化,并且U1的第二个标准转换序列优于U2的第二个标准转换序列.
第二标准转化箱子operator bool()是bool至bool(标识转换),而在情况下,第二标准的转换operator int ()是int到bool这是一个布尔值转换.
因此,operator bool ()如果两个转换函数共享相同的const限定,则使用转换序列会更好.