Fed*_*dor 7 c++ overloading multiple-inheritance language-lawyer c++20
在下面的简化程序中, structC继承自两个 structA和B。前者定义了 spaceshipoperator <=>和 less 运算符,后者 \xe2\x80\x93 仅定义了 spaceship 运算符。然后对 class 的对象执行更少的操作C:
#include <compare>\n\nstruct A { \n auto operator <=>(const A&) const = default;\n bool operator <(const A&) const = default;\n};\nstruct B { \n auto operator <=>(const B&) const = default; \n};\nstruct C : A, B {};\nint main() { \n C c;\n c.operator<(c); //ok everywhere\n return c < c; //error in GCC\n}\nRun Code Online (Sandbox Code Playgroud)\n这里令人惊讶的是,显式调用c.operator<(c)在所有编译器中都成功,但类似的调用c<c被 Clang 允许,但在 GCC 中被拒绝:
error: request for member \'operator<=>\' is ambiguous\n<source>:8:10: note: candidates are: \'auto B::operator<=>(const B&) const\'\n<source>:4:10: note: \'constexpr auto A::operator<=>(const A&) const\'\nRun Code Online (Sandbox Code Playgroud)\n演示: https: //gcc.godbolt.org/z/xn7W9PaPc
\n有一个可能相关的问题:GCC无法区分operator++()和operator++(int)。但在该问题中,显式运算符 (++) 调用被所有编译器拒绝,与此问题不同,显式运算符调用被所有编译器接受。
\n我认为operator <中只有一个C,源自A,并且 Starship Operator 根本不予考虑。是这样吗?这里的编译器是什么?
gcc 这里是正确的。
当你这样做时:
c.operator<(c);
Run Code Online (Sandbox Code Playgroud)
您正在对字面上名为 的内容执行名称查找operator<。只有一个这样的函数( 中的函数A),因此成功。
但是当你这样做时c < c,你并不是在查找operator<. 你正在做两件事:
c < c查找operator<候选者(成员、非成员或内置)的特定查找c <=> c现在,第一次查找成功并找到A::operator<与之前相同的结果。但第二次查找失败 - 因为c <=> c不明确(在A和中的候选者之间B)。来自[class.member.lookup]/6 的规则是:
搜索的结果是 的声明集
S(N,T)。如果它是无效集,则程序格式错误。
搜索结果是一个无效的集合,因此程序格式不正确。并不是我们什么也没找到,而是整个查找失败了。仅仅因为在这种情况下我们正在查找重写的候选者而不是主要候选者并不重要,它仍然是失败的查找。
它失败实际上是件好事,因为如果我们以通常的方式解决这个不明确的合并集问题:
struct C : A, B {
+ using A::operator<=>;
+ using B::operator<=>;
};
Run Code Online (Sandbox Code Playgroud)
那么我们的查找就会不明确!因为现在我们对重写候选者的查找找到了两个operator<=>s,所以我们最终得到了三个候选者:
operator<(A const&, A const&)operator<=>(A const&, A const&)operator<=>(B const&, B const&)1优于2(因为主要候选优于重写的候选),但1vs3不明确(两者都不优于另一个)。
因此,原版失败了,这个也失败了,这是一件好事:作为课程作者,你有责任想出正确的做法 - 因为这是什么并不明显。