【问题标题】:Inherited synthesized comparison operator produces warning/error: ISO C++20 considers use of overloaded operator '!='继承的综合比较运算符产生警告/错误:ISO C++20 考虑使用重载运算符 '!='
【发布时间】:2021-04-26 04:53:40
【问题描述】:

在下面的代码中 sn -p clang 11.0.1 会产生一个警告

template <class T>
struct iterator_facade
{
    template<class S>
    bool operator==(const S &other) const noexcept
    {
        return static_cast<const T &>(*this).equal_to(other);
    }
};

struct iterator : public iterator_facade<iterator>
{
    bool equal_to(const iterator &) const noexcept
    {
        return true;
    }
};

bool check(iterator a, iterator b)
{
    return a == b;
}

实时代码: https://godbolt.org/z/65zWEq

source>:21:14: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'iterator' and 'iterator') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
    return a == b;
           ~ ^  ~
<source>:5:7: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
        bool operator==(const S &other) const noexcept
             ^

上面的代码可以使用 Visual C++ (VS 16.8.x) 和以前的预览版 (VS 16.9.0 Preview 2) 成功编译。但是,最近发布的 VS 16.9.0 Preview 3 现在会为此代码 sn-p 产生错误:

1>C:\MyProjects\test\test\source.cpp(21,16): error C2666: 'foo<bar>::operator ==': 2 overloads have similar conversions
1>C:\MyProjects\test\test\source.cpp(5,7): message : could be 'bool iterator_facade<iterator>::operator ==<bar>(const S &) noexcept const' [rewritten expression '!(x == y)']
1>        with
1>        [
1>            S=iterator
1>        ]
1>C:\MyProjects\test\test\source.cpp(5,7): message : or 'bool iterator_facade<iterator>::operator ==<iterator>(const S &) noexcept const' [synthesized expression '!(y == x)']
1>        with
1>        [
1>            S=iterator
1>        ]
1>C:\MyProjects\test\test\source.cpp(21,16): message : while trying to match the argument list '(iterator, iterator)'

似乎没有合规的方式为派生类 iterator 和 CRTP 类 iterator_facade 提供综合比较运算符?

【问题讨论】:

  • 综合运算符的全部意义在于它很容易做到,您不需要在基类中进行。只需给 bar 一个与 equal_to 完全相同的 operator== 重载就可以了。
  • 这个例子无论如何都不会在 C++17 中编译。你有一个更完整的例子来说明你的想法吗? foo 也应该支持异构比较,还是只支持同质比较?
  • 这里是以下iterator_facade 实现的简化:https://github.com/vector-of-bool/neo-fun/blob/develop/src/neo/iterator_facade.hpp 该类的重点是在基类中实现这些运算符:)
  • 这个迭代器外观类尝试通过派生类的equal_todistance_to 成员(无论哪个可用)来实现所需的迭代器比较运算符。旧版本通过实现通常的 ==、!= 等来实现。一段时间后,作者改用 C++20 综合运算符,简化了代码。它曾经一直工作到 VS 16.9.0 Preview 3。它开始产生一个错误,我将其简化为上面的代码 sn-p。

标签: c++ c++20 spaceship-operator


【解决方案1】:

问题是我们有这个比较运算符:

template<class T>
struct iterator_facade
{
    template <class S>
    bool operator==(const S &other) const noexcept;
};

所以当我们尝试比较两个iterators 时,我们有这两个候选者:

bool operator==(iterator_facade<iterator> const&, iterator const&); // the member candidate
bool operator==(iterator const&, iterator_facade<iterator> const&); // the reversed member candidate

问题在于,候选成员在第二个参数中是完全匹配的,但在第一对中是派生到基础的转换......而反转的候选者是在第一个参数中完全匹配,但派生 -在第二个中进行到基础转换。这意味着没有一个候选人比另一个更好,而且两者都是模棱两可的。

现在,由于这个库实际上需要 C++20 开始,这有点毫无意义。只需让iterator 自行实现operator==,这会增加......什么都没有?我不确定它到底添加了什么。

但如果你真的想让它工作,你需要提供第二个重载,它需要两个派生实例。如:

template <class T>
struct iterator_facade
{
    friend bool operator==(T const& a, T const& b) noexcept {
        return a.equal_to(b);
    }

    template <sentinel_for<T> S>
    bool operator==(S const& other) const noexcept;
};

同构运算符将比异种运算符更好地匹配,因此比较可以正常编译。

【讨论】:

  • 啊,我明白了。有一个确切的两种类型的朋友比较运算符实际上解决了歧义。非常感谢你,巴里!
  • 嗯,但是cppreference“在这种情况下,重载解决方案有一个最终决胜局,更喜欢未重写的候选者而不是重写的候选者”,这意味着不应该有歧义吗?
  • @HolyBlackCat Tiebreakers 只有在转换序列相等时才会发挥作用(请参阅那里的“最佳可行函数”部分)。
猜你喜欢
  • 2021-02-05
  • 2021-09-05
  • 2011-12-05
  • 1970-01-01
  • 1970-01-01
  • 2012-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多