Apple clang and C++20 operator ambiguity with inherited comparison operator

Viewed 490

Upgraded Xcode today (and underlying clang went up to clang-1200.0.32.21), and started getting ambiguous comparison errors like described here. But in that example the lack of const was evident, while for me the issue seems to be an inherited comparison operator. Here is a minimal example:

struct Bar
{
    bool operator==(const Bar&) const
    {
        return true;
    }
};

struct Foo : Bar
{
    using Bar::operator==;
#if defined(USE_FOO)
    bool operator==(const Foo&) const
    {
        return true;
    }
#endif
};

int main()
{
    Foo a,b;
    if (a == b)
    {
        return 0;
    }
    else 
    {
        return 1;
    }
}

So, when compiling with clang++ -std=c++2a it gives:

warning: ISO C++20 considers use of overloaded operator '=='
      (with operand types 'Foo' and 'Foo') to be ambiguous despite there being a
      unique best viable function [-Wambiguous-reversed-operator]
    if (a == b)
        ~ ^  ~
test.cpp:3:10: note: ambiguity is between a regular call to this operator and a
      call with the argument order reversed
    bool operator==(const Bar&) const
         ^

while clang++ -std=c++2a -DUSE_FOO works.

Is there a legitimate cause that breaks use of inherited operators or is this an Apple clang bug?

1 Answers

It's the using declaration. If you omit it, everything works as expected. The operator defined in the base class is found by name lookup, provides a single candidate (same implicit conversions, with the order reversed) and all is well. Here's what the using declaration does

[over.match.funcs]

4 For non-conversion functions introduced by a using-declaration into a derived class, the function is considered to be a member of the derived class for the purpose of defining the type of the implicit object parameter.

Basically, the using declaration behaves as if you declared

bool operator==(const Bar&) const

In Foo. So the left argument is considered a Foo when name lookup finds operator==. This member, when re-written, provides two candidates

bool operator==(const Foo&, const Bar&);
bool operator==(const Bar&, const Foo&);

And now we have the exact same problem the other question has. Not with a const-qualification conversion, but a derived-to-base conversion.

Related