不同的编译器调用不同的演员操作符
考虑下面这个简短的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; }
如果我在不同的编译器上编译它,我会得到不同的结果。 使用Clang 3.4和GCC 4.4.7它打印true
,而Visual Studio 2013打印false
,这意味着他们在(bool)b
调用不同的演员操作。 根据标准哪个是正确的行为?
在我的理解operator bool()
不需要转换,而operator int()
需要一个int
来bool
转换,所以编译器应该select第一个。 const
是否做了这样的事情,const转换被编译器认为更“昂贵”吗?
如果我删除了const
,那么所有编译器同样产生false
作为输出。 另一方面,如果我将两个类组合在一起(两个运算符将在同一个类中),那么所有三个编译器将产生true
输出。
标准规定:
除非两个函数转换为相同types,否则派生类中的转换函数不会隐藏基类中的转换函数。
§12.3[class.conv]
这意味着operator bool
不被operator int
隐藏。
标准规定:
在重载parsing期间,隐含的对象参数与其他参数无法区分。
§13.3.3.1[over.match.funcs]
在这种情况下的“隐含的对象论证”是b
,它是B2 &
的types。 operator bool
需要const B2 &
,因此编译器必须将const添加到b
才能调用operator bool
。 这 – 所有其他的东西是相等的 – 使operator int
更好地匹配。
该标准规定static_cast
(在这种情况下,C风格转换正在执行)可以转换为typesT
(在本例中为int
),如果:
声明
T t(e);
对于一些发明的临时variablest
是forms良好的。§5.2.9[expr.static.cast]
因此int
可以转换为bool
,而bool
可以转换为bool
。
标准规定:
考虑
S
及其基类的转换函数。 那些不隐藏在S
非显式转换函数和产出typesT
或者可以通过标准转换序列转换为typesT
types是候选函数。§13.3.1.5[over.match.conv]
所以重载集合由operator int
和operator bool
。 所有其他的东西是相等的, operator int
是一个更好的匹配(因为你不必添加const)。 因此应该selectoperator int
。
注意(也许与直觉相反),一旦标准被添加到过载集合(如上所述),标准就不考虑返回types(即这些操作符所转换的types),只要其中一个参数的转换顺序它们优于另一个参数的转换序列(由于常数,在这种情况下是这种情况)。
标准规定:
给定这些定义,如果对于所有参数i,ICSi(F1)不是比ICSi(F2)更差的转换序列,则可行函数F1被定义为比另一个可行函数F2更好的函数,然后
- 对于一些论元j,ICSj(F1)是比ICSj(F2)更好的转换序列,否则,
- 上下文是由用户定义的转换进行的初始化,并且从返回typesF1到目标types(即被初始化的实体的types)的标准转换序列是比来自返回types的标准转换序列更好的转换序列F2的目标types。
§13.3.3[over.match.best]
在这种情况下,只有一个参数(隐含的this
参数)。 B2 &
=> B2 &
的转换序列(要调用operator int
)优于B2 &
=> const B2 &
(调用operator bool
),因此operator int
是从重载集中select的,而不考虑它的事实实际上不直接转换成bool
。
短
由于b
不是const限定的,因此转换函数operator int()
是通过在operator bool() const
进行clangselect的,而bool的转换运算符是。
简单的推理是当将b
转换为bool
时,候选函数用于重载分辨率(具有隐式对象参数)
operator bool (B2 const &); operator int (B2 &);
第二个是更好的匹配,因为b
不是const限定的。
如果两个函数共享相同的资格(不论是否是const
),则selectoperator bool
,因为它提供了直接转换。
通过铸造符号转换,逐步分析
如果我们同意根据[ostream.inserters.arithmetic]中的boolean ostream插入器(std :: basic_ostream :: operator <<(bool val))调用带有从b
到bool
的转换结果的值,我们可以挖掘该转换。
1.演员expression
b对bool的铸造
(bool)b
评估
static_cast<bool>(b)
按照C ++ 11,5.4 / 4 [expr.cast],因为const_cast
不适用(不是在这里添加或删除const)。
如果使用bool t(b);
,则每个C ++ 11,5.2.9 / 4 [expr.static.cast]允许静态转换bool t(b);
对于一个发明的variablest是很好的形成。 这样的语句按照C ++ 11,8.5 / 15 [dcl.init]被称为直接初始化。
2.直接初始化bool t(b);
最less提到的标准段落的第16条(重点是我的):
初始化器的语义如下。 目标types是被初始化的对象或引用的types,源types是初始化expression式的types。
[…]
如果源types是(可能是cv-qualified) 类types,则考虑转换函数 。
枚举适用的转换函数,最好通过重载parsing来select。
2.1哪些转换function可用?
可用的转换函数是operator int ()
和operator bool() const
因为C ++ 11,12.3 / 5 [class.conv]告诉我们:
除非两个函数转换为相同types,否则派生类中的转换函数不会隐藏基类中的转换函数。
而C ++ 11,13.3.1.5/1 [over.match.conv]指出:
考虑S及其基类的转换函数。
其中S是将被转换的类。
2.2哪些转换function适用?
C ++ 11,13.3.1.5/1 [over.match.conv] (强调我的):
1 […]假设“cv1 T”是被初始化的对象的types,“cv S”是初始化expression式的types,S是类types,候选函数被select如下:考虑S及其基类的函数。 那些不隐藏在S中的非显式转换函数和产出typesT 或者可以通过标准转换序列转换为typesT的types是候选函数。
因此, operator bool () const
是可用的,因为它并不隐藏在B2
并产生一个bool
。
在最后一个标准报价中强调的部分与使用operator int ()
进行转换相关,因为int
是可以通过标准转换序列转换为bool的types。 从int
到bool
的转换甚至不是一个序列,而是每个C ++ 11允许的直接转换,4.12 / 1 [conv.bool]
算术,非范型枚举,指针或指向成员types的指针可以转换为booltypes的prvalue。 零值,空指针值或空成员指针值被转换为false; 任何其他值都转换为true。
这意味着operator int ()
也适用。
2.3select了哪种转换function?
通过重载分辨率( C ++ 11,13.3.1.5/1 [over.match.conv] )来select适当的转换函数:
重载parsing用于select要调用的转换函数。
当涉及类成员函数的重载parsing时,有一个特殊的“怪癖”:隐式对象参数“。
Per C ++ 11,13.3.1 [over.match.funcs] ,
静态和非静态成员函数都有一个隐含的对象参数[…]
其中非静态成员函数的这个参数的types – 根据子句4-是:
对于不带ref-qualifier或带有&ref-qualifier声明的函数,“左值引用cv X”
用&& ref-qualifier声明的函数的“右值引用cv X”
其中X是函数所属的类, cv是成员函数声明中的cv限定。
这意味着(按照C ++ 11,13.3.1.5 /2 [over.match.conv] ),在一个初始化转换函数中,
他的参数列表有一个参数,它是初始化expression式。 [注:该参数将与转换函数的隐式对象参数进行比较。 – 注意]
重载parsing的候选函数是:
operator bool (B2 const &); operator int (B2 &);
显然,如果使用B2
types的非常量对象来请求转换, operator int ()
会更好,因为operator bool ()
需要进行限定转换。
如果两个转换函数都具有相同的const限定条件,那么这些函数的重载parsing就不会再起作用了。 在这种情况下,转换(顺序)排名就到位了。
3.当两个转换函数共享相同的const限定时,为什么selectoperator bool ()
?
从B2
到bool
的转换是用户定义的转换序列( C ++ 11,13.3.3.1.2 / 1 [over.ics.user] )
用户定义的转换序列由一个初始的标准转换序列,后跟一个用户定义的转换,后跟一个第二个标准转换序列组成。
[…]如果用户定义的转换由转换函数指定,则初始标准转换序列将源types转换为转换函数的隐式对象参数。
C ++ 11,13.3.3.2/3 [over.ics.rank]
[…]根据关系更好的转换序列和更好的转换来定义隐式转换序列的偏序。
如果用户定义的转换序列U1包含相同的用户定义的转换函数或构造函数或聚合初始化,并且U1的第二标准转换序列优于U2的第二个标准转换序列。
第二个标准转换是operator bool()
是bool
到bool
(标识转换)的情况,而operator int ()
情况下的第二个标准转换是int
到布尔转换的bool
。
因此,如果两个转换函数共享相同的const限定,则使用operator bool ()
的转换序列更好。
C ++ booltypes有两个值 – true和false,对应的值为1和0.如果在B2类中添加一个调用基类(B)的bool操作符的bool操作符,可以避免内在的混淆,然后输出作为错误。 这是我的修改程序。 然后运算符布尔意味着运算符布尔,而不是以任何方式运算符int。
#include <iostream> class B { public: operator bool() const { return false; } }; class B2 : public B { public: operator int() { return 5; } operator bool() { return B::operator bool(); } }; int main() { B2 b; std::cout << std::boolalpha << (bool)b << std::endl; }
在你的例子中,(bool)b试图为B2调用bool操作符,B2inheritance了bool操作符,int操作符通过支配规则,int操作符被调用,B2中inheritance了bool操作符。 但是,通过在B2类中明确拥有一个bool操作符,问题就解决了。
以前的一些答案,已经提供了很多信息。
我的贡献是“cast操作”被编译成类似的,对于“重载操作”,我build议用每个操作的唯一标识符来做一个函数,然后用所需的操作符或typesreplace它。
#include <iostream> class B { public: bool ToBool() const { return false; } }; class B2 : public B { public: int ToInt() { return 5; } }; int main() { B2 b; std::cout << std::boolalpha << b.ToBool() << std::endl; }
然后,应用操作员或演员。
#include <iostream> class B { public: operator bool() { return false; } }; class B2 : public B { public: operator int() { return 5; } }; int main() { B2 b; std::cout << std::boolalpha << (bool)b << std::endl; }
只是我2美分。