Inherited synthesized comparison operator produces warning/error: ISO C++20 considers use of overloaded operator '!='

Inherited synthesized comparison operator produces warning/error: ISO C++20 considers use of overloaded operator '!='

在以下代码片段中,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 预览版 3 现在会为此代码段产生错误:

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)'

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

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

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

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

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;
};

同构运算符比异构运算符更匹配,因此比较会编译正常。