operator bool
在以下示例中中断了operator<
的使用。任何人都可以解释为什么bool
在if (a < 0)
表达式中与特定运算符一样重要,是否有解决方法?
struct Foo {
Foo() {}
Foo(int x) {}
operator bool() const { return false; }
friend bool operator<(const Foo& a, const Foo& b) {
return true;
}
};
int main() {
Foo a, b;
if (a < 0) {
a = 0;
}
return 1;
}
当我编译时,我得到:
g++ foo.cpp
foo.cpp: In function 'int main()':
foo.cpp:18:11: error: ambiguous overload for 'operator<' (operand types are 'Foo' and 'int')
if (a < 0) {
^
foo.cpp:18:11: note: candidate: operator<(int, int) <built-in>
foo.cpp:8:17: note: candidate: bool operator<(const Foo&, const Foo&)
friend bool operator<(const Foo& a, const Foo& b)
答案 0 :(得分:24)
这里的问题是C ++有两个处理a < 0
表达式的选项:
a
转换为bool
,并将结果与0
内置运算符<
进行比较(一次转化)0
转换为Foo
,并将结果与您定义的<
进行比较(一次转化)这两种方法都等同于编译器,因此它会发出错误。
您可以通过在第二种情况下删除转换来明确这一点:
if (a < Foo(0)) {
...
}
答案 1 :(得分:13)
重点是:
首先,operator <
有两个相关的重载。
operator <(const Foo&, const Foo&)
。使用此重载需要使用0
将文字Foo
转换为Foo(int)
的用户定义。operator <(int, int)
。使用此重载需要使用用户定义的Foo
将bool
转换为operator bool()
,然后将促销转换为int
(这是标准的) ,正如Bo Persson所指出的那样,与转换不同。这里的问题是:模糊性从何而来?当然,第一次呼叫只需要用户定义的转换,比第二次呼叫更明智,这需要用户定义的转换,然后是促销?
但事实并非如此。该标准为每个候选人分配一个等级。但是,“用户定义的转换后跟促销”没有等级。这与仅使用用户定义的转换具有相同的等级。简单地(但非正式地),排名顺序看起来有点像这样:
float
到int
)(免责声明:如上所述,这是非正式的。当涉及多个参数时,它会变得更加复杂,我也没有提及参考或cv资格。这只是一个粗略的概述。)
所以希望这可以解释为什么这个电话不明确。现在就如何解决这个问题的实际部分。 几乎从不提供operator bool()
的人希望在涉及整数算术或比较的表达式中隐式使用它。在C ++ 98中,有一些模糊的解决方法,从std::basic_ios<CharT, Traits>::operator void *
到“改进的”更安全的版本,包括指向成员的指针或不完整的私有类型。幸运的是,在隐式使用operator bool()
之后,C ++ 11引入了一种更可读且更一致的方法来阻止整数提升,即将运算符标记为explicit
。这将完全消除operator <(int, int)
过载,而不仅仅是“降级”。
正如其他人所提到的,您也可以将Foo(int)
构造函数标记为显式。这将消除operator <(const Foo&, const Foo&)
重载的相反效果。
第三种解决方案是提供额外的重载,例如:
operator <(int, const Foo&)
operator <(const Foo&, int)
在这个示例中,后者将优先于上述重载作为完全匹配,即使您没有引入explicit
。同样的事情,例如对
operator <(const Foo&, long long)
优先于operator <(const Foo&, const Foo&)
中的a < 0
,因为它的使用只需要促销。
答案 2 :(得分:4)
因为编译器无法在bool operator <(const Foo &,const Foo &)
和operator<(bool, int)
之间进行选择,这两种情况都符合这种情况。
为了解决问题,请制作第二个构造函数explicit
:
struct Foo
{
Foo() {}
explicit Foo(int x) {}
operator bool() const { return false; }
friend bool operator<(const Foo& a, const Foo& b)
{
return true;
}
};
修改强>
好的,最后我得到了一个问题的真正要点:) OP问为什么他的编译器提供operator<(int, int)
作为候选人,但 “不允许多步转换” < / strong>即可。
<强>答案:强>
是的,要调用operator<(int, int)
对象a
需要转换Foo -> bool -> int
。 但,C ++标准实际上并没有说“多步转换是非法的”。
§12.3.4[class.conv]
最多一个用户定义的转换(构造函数或转换 function)隐式应用于单个值。
bool
到int
不是用户定义的转换,因此它是合法的,编译器有权选择operator<(int, int)
作为候选。
答案 3 :(得分:2)
这正是编译器告诉你的。
解决编译器if (a < 0)
的一种方法是使用您提供的Foo(int x)
构造函数从0创建对象。
第二个是使用operator bool
转换,并将其与int
(促销)进行比较。您可以在Numeric promotions部分了解有关它的更多信息。
因此,它对于编译器来说是不明确的,它无法决定你希望它走哪条路。