我最近注意到一些运算符重载的行为,我自己无法理解。以下两个类只在ClassA
的成员比较运算符重载中的const
上有所不同,在ClassB
中它们没有const
。通常我知道人们总是更喜欢有const
的那个,但我还是对下面我将描述的行为感兴趣。
#include <string>
class ClassA {
public:
explicit ClassA(double t) : _t(t) {}
std::string operator<=(int const& other) const {
return "A(<=)";
}
std::string operator==(int const& other) const {
return "A(==)";
}
friend std::string operator<=(int const& other, ClassA const& expr) {
return "A'(<=)";
}
friend std::string operator==(int const& other, ClassA const& expr) {
return "A'(==)";
}
private:
double _t;
};
class ClassB {
public:
explicit ClassB(double t) : _t(t) {}
std::string operator<=(int const& other) {
return "B(<=)";
}
std::string operator==(int const& other) {
return "B(==)";
}
friend std::string operator<=(int const& other, ClassB const& expr) {
return "B'(<=)";
}
friend std::string operator==(int const& other, ClassB const& expr) {
return "B'(==)";
}
private:
double _t;
};
现在我想在
const
和非const
的情况下使用这些类和比较函数。int
main(int argc,
char* argv[]) {
ClassA a1{0};
1==a1; //OK
1<=a1; //OK
ClassA const a2{0};
1==a2; //OK
1<=a2; //OK
ClassB b1{0};
1==b1; //NOT OK
1<=b1; //OK
ClassB const b2{0};
1==b2; //OK
1<=b2; //OK
return 0;
}
所有的东西都很好,但是我标记为NOT OK
的那一行有编译错误。
error C2446: '==': no conversion from 'ClassB' to 'int'
我的问题分为三个部分,但我期望有一个好的理由可以回答所有这些问题。所以我希望把它们放在一个单独的SO问题中。
当不等式运算符`<=`能编译通过时,为什么相等运算符`==`不能编译通过?对于友元函数来说,成员函数是否是`const`很重要吗?为什么使`ClassB`对象成为`const`就能解决问题?
更新:
1. 在评论区@Eljay指出,问题可能是由C++20的一个新特性引起的,该特性会自动生成具有反向参数的比较运算符。这显然使得成员函数`std::string operator==(int const& other)`(重新排列后)更适合于`1==b1`。经过一番搜索,我找到了规则,说明这些应该在overload resolution的规则中生成。
2. 重写的候选方法:
a. 对于四个关系运算表达式xy和x>=y,将找到的所有成员、非成员和内置operator<=>添加到集合中。 b. 对于四个关系运算表达式xy和x>=y以及三路比较表达式x<=>y,对于找到的每个成员、非成员和内置operator<=>,都会添加一个候选方法,其参数顺序被翻转。 c. 对于x!=y,将找到的所有成员、非成员和内置operator==添加到集合中。 d. 对于相等运算符表达式x==y和x!=y,对于找到的每个成员、非成员和内置operator==,都会添加一个参数顺序被翻转的候选方法。
3. 在所有情况下,重写的候选方法都不考虑在重写表达式的上下文中。对于其他所有运算符,重写的候选方法集为空。
@463035818_is_not_a_number指出了关于不同版本编译器能否编译代码的一些有趣发现。特别是对于带标志
-std=c++2a
的clang
和gcc
,最新版本的x86-64 clang 12.0.0
和x86-64 gcc 11.1
不能编译,而旧版本的x86-64 clang 9.0.1
和x86-64 gcc 9.4
可以编译。对于VisualStudio,我们使用标志/std:c++latest
看到类似的模式。这里最新版本的x64 msvc v19.28(VS16.9)
不能编译,而直接前身x64 msvc v19.28
可以编译。这些测试是使用Compiler explorergodbolt.org进行的。特别有趣的是,
clang
和gcc
的编译器错误表明问题在于std::string operator==(int const& other)
没有返回bool
。
clang
error: return type 'std::string' (aka 'basic_string<char>') of selected 'operator==' function for rewritten '==' comparison is not 'bool'
1==b1; //NOT OK
gcc
error: return type of 'std::string ClassB::operator==(const int&)' is not 'bool'
1==b1; //NOT OK
尽管这些都是非常有趣的见解,但原始问题仍然没有得到解答。
operator==
没有返回一个bool
。这会导致 C++20 中的 spaceship 合成器不满意。 - Eljayconst
的B
正在促使比较合成,但由于operator==
没有适当的语义,因此失败了。而且,可能是因为缺少<=>
,所以<=
可以正常工作,并成功使用自由函数operator<=
。 - Nathan Piersonbool operator==(int const&)
比友元operator==
更适合于1==b1; //NOT OK
,因为friend
有一个const
ClassB引用而b1
不是const。 - Eljay