【发布时间】:2021-06-11 14:08:36
【问题描述】:
我最近注意到一些我自己无法弄清楚的运算符重载行为。以下两个类仅在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 和非常量场景中使用这些类和比较函数。
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修复它?
更新:
- 在 cmets 中,@Eljay 指出问题可能是由新的 C++20 功能造成的,该功能会自动生成带有倒置参数的比较运算符。这显然使成员
std::string operator==(int const& other)(重新安排后)更适合1==b1。经过一番挖掘,我发现规则说这些应该在overload resolution的规则中生成。
- 重写的候选人:
- 对于四个关系运算符表达式 x
y 和 x>=y,找到的所有成员、非成员和内置运算符 都是 添加到集合中。 - 对于四个关系运算符表达式 x
y 和 x>=y 以及三路比较表达式 xy,a 两个参数顺序颠倒的合成候选是 为找到的每个成员、非成员和内置运算符 添加。 - 对于 x!=y,所有找到的成员、非成员和内置运算符 == 都将添加到集合中。
- 对于等式运算符表达式 x==y 和 x!=y,添加两个参数顺序颠倒的合成候选 已找到每个成员、非成员和内置 operator==。
在所有情况下,都不会在上下文中考虑重写的候选者 的重写表达式。对于所有其他运算符,重写 候选集为空。
-
@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 explorer godbolt.org 进行的。 -
特别有趣的是
clang和gcc的编译器错误提示问题是std::string operator==(int const& other)没有返回bool。
叮当声
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 宇宙飞船合成器脾气暴躁。 -
我的 C++20 intellisense 给了我一个更丰富的答案:
function "ClassB::operator==" ... selected for operator rewrite does not return type bool。看起来非constB正在提示比较合成失败,因为operator==没有适当的语义。而<=大概是可以工作的,因为<=>的缺失意味着它甚至没有尝试合成<=并成功使用了免费功能operator<=。 -
我认为发生的事情是 ClassB 成员
bool operator==(int const&)(在宇宙飞船合成器和重新编曲之后)比朋友operator==更适合1==b1; //NOT OK,因为friend有constClassB 引用和b1不是 const。 -
@Swift-FridayPie godbolt.org/z/cM7K1a6Mn。我想我会添加
language-lawyer -
@SimonT 稍微改一下你的问题怎么样?考虑一下:godbolt.org/z/cM7K1a6Mn。您的代码使用 gcc 和 clang 与旧版本编译,但都拒绝最新版本中的代码。它几乎看起来像是一个错误/缺失的功能,并且最近被“修复”了。另一方面,“
operator==的返回类型不是bool”作为错误看起来很奇怪,我不想相信它。也许还添加language-lawyer
标签: c++ compiler-errors operator-overloading language-lawyer c++20