gpt4 book ai didi

c++ - 使用非 bool 返回值重载相等比较时,C++20 中的重大变化或 clang-trunk/gcc-trunk 中的回归?

转载 作者:行者123 更新时间:2023-12-01 09:23:52 24 4
gpt4 key购买 nike

以下代码在 c++17 模式下使用 clang-trunk 编译得很好,但在 c++2a(即将推出的 c++20)模式下会中断:

// Meta struct describing the result of a comparison
struct Meta {};

struct Foo {
Meta operator==(const Foo&) {return Meta{};}
Meta operator!=(const Foo&) {return Meta{};}
};

int main()
{
Meta res = (Foo{} != Foo{});
}

它也可以用 gcc-trunk 或 clang-9.0.0 很好地编译: https://godbolt.org/z/8GGT78

clang-trunk 和 -std=c++2a 的错误:
<source>:12:19: error: use of overloaded operator '!=' is ambiguous (with operand types 'Foo' and 'Foo')
Meta res = (f != g);
~ ^ ~
<source>:6:10: note: candidate function
Meta operator!=(const Foo&) {return Meta{};}
^
<source>:5:10: note: candidate function
Meta operator==(const Foo&) {return Meta{};}
^
<source>:5:10: note: candidate function (with reversed parameter order)

我知道 C++20 可以只重载 operator==并且编译器会自动生成 operator!=通过否定 operator== 的结果.据我了解,这仅在返回类型为 bool 时才有效。 .

问题的根源在于我们在 Eigen 中声明了一组运算符 == , != , < , ... 之间 Array对象或 Array和标量,它们返回(的表达式) bool 的数组(然后可以按元素访问,或以其他方式使用)。例如。,
#include <Eigen/Core>
int main()
{
Eigen::ArrayXd a(10);
a.setRandom();
return (a != 0.0).any();
}

与我上面的示例相反,这甚至无法使用 gcc-trunk: https://godbolt.org/z/RWktKs .
我还没有设法将其简化为非特征示例,该示例在 clang-trunk 和 gcc-trunk 中均失败(顶部的示例非常简化)。

相关问题报告: https://gitlab.com/libeigen/eigen/issues/1833

我的实际问题:这实际上是 C++20 中的重大变化(并且是否有可能重载比较运算符以返回元对象),还是更可能是 clang/gcc 中的回归?

最佳答案

是的,代码实际上在 C++20 中中断了。
表达式 Foo{} != Foo{}在 C++20 中有三个候选者(而在 C++17 中只有一个):

Meta operator!=(Foo& /*this*/, const Foo&); // #1
Meta operator==(Foo& /*this*/, const Foo&); // #2
Meta operator==(const Foo&, Foo& /*this*/); // #3 - which is #2 reversed
这来自 [over.match.oper]/3.4 中重写的新候选规则。 .所有这些候选人都是可行的,因为我们的 Foo参数不是 const .为了找到最可行的候选人,我们必须通过我们的决胜局。
最佳可行功能的相关规则来自 [over.match.best]/2 :

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

  • [... lots of irrelevant cases for this example ...] or, if not that, then
  • F2 is a rewritten candidate ([over.match.oper]) and F1 is not
  • F1 and F2 are rewritten candidates, and F2 is a synthesized candidate with reversed order of parameters and F1 is not
#2#3是重写的候选者,和 #3参数的顺序颠倒了,而 #1没有被重写。但是为了达到那个决胜局,我们需要首先通过那个初始条件:对于所有参数,转换序列并不差。 #1优于 #2因为所有的转换序列都是一样的(简单来说,因为函数参数是一样的)和 #2是重写的候选者,而 #1不是。
但是...两对 #1/ #3#2/ #3卡在第一个条件上。在这两种情况下,第一个参数对于 #1 都有更好的转换顺序。/ #2而第二个参数对于 #3 有更好的转换顺序(参数 const 必须经过额外的 const 限定,因此它的转换顺序更差)。此 const触发器使我们无法偏爱任何一个。
因此,整个重载决议是模棱两可的。

As far as I understand, this only works as long as the return type is bool.


那不正确。我们无条件地考虑重写和逆转的候选者。我们的规则是,来自 [over.match.oper]/9 :

If a rewritten operator== candidate is selected by overload resolution for an operator @, its return type shall be cv bool


也就是说,我们仍然考虑这些候选人。但如果最佳可行的候选者是 operator==例如,返回 Meta - 结果与删除该候选人基本相同。
我们不想处于重载解析必须考虑返回类型的状态。无论如何,这里的代码返回 Meta 的事实无关紧要 - 如果返回 bool,问题也会存在.

值得庆幸的是,这里的修复很简单:
struct Foo {
Meta operator==(const Foo&) const;
Meta operator!=(const Foo&) const;
// ^^^^^^
};
一旦你同时使用了两个比较运算符 const ,不再有歧义。所有参数都相同,因此所有转换序列几乎相同。 #1现在将击败 #3 by not by 重写和 #2现在将击败 #3通过不被逆转 - 这使得 #1最佳可行人选。与我们在 C++17 中的结果相同,只需多走几步即可实现。

关于c++ - 使用非 bool 返回值重载相等比较时,C++20 中的重大变化或 clang-trunk/gcc-trunk 中的回归?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60568088/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com