4

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?

Rudolfs Bundulis
  • 11,636
  • 6
  • 33
  • 71
  • There is a lot of code that breaks with the introduction of rewritten comparison operators. The general guideline is that the original code likely didn't have clear/reasonable semantics. That being said, it's not immediately apparent to me what's wrong with your code. Using a base class `operator==` should be fine. Maybe :p – cigien Oct 22 '20 at 13:49
  • @cigien Well, yeah, I can clearly see how enforcing proper constness (as in the linked sample) etc could and maybe should break stuff, but here I'm just puzzled. – Rudolfs Bundulis Oct 22 '20 at 13:51
  • Not just apple clang: https://godbolt.org/z/h84K3r – Alan Birtles Oct 22 '20 at 13:54
  • I see you've accepted the answer below. But is your question, "what are the new rules that break the code?", or "*why* do the new rules break my code, what *was* I doing wrong?" I've asked the [latter](https://stackoverflow.com/questions/64130311/what-are-the-breaking-changes-caused-by-rewritten-comparison-operators), but the answers there, as well as the answer here, basically address the former. – cigien Oct 22 '20 at 14:05
  • @cigien I was interested in the former, since I was not aware how exactly does the type interchangeability mess stuff up. I can kinda accept that new standards introduce breaking changes (ofc it's sad), even though in my real case, where `using` was used to disambiguate operators from multiple parent classes, this is quite impacting. – Rudolfs Bundulis Oct 22 '20 at 14:12
  • No, the language is actually exceedingly careful to not break code with new standards, unless there's a really good reason. I know there are good reasons for the breaking changes caused by new comparison operator rules, I just don't know the scope of the changes. – cigien Oct 22 '20 at 14:15
  • @cigien I'd actually say that your case is worse, since I actually got an error, while in your case it's a run-time difference, so in that case, I can agree the explanation to why is more important. – Rudolfs Bundulis Oct 22 '20 at 14:17
  • Well, the intent of the question was broad, so I added an (admittedly) contrived example to that question. Unfortunately, everyone got (understandably) hung up on explaining why that particular example breaks. Oh well, eventually there will be enough examples around to motivate people to address the underlying question. By my estimate, this is the 5th question of this nature, and I'm sure more will come along :) – cigien Oct 22 '20 at 14:24

1 Answers1

4

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Thanks, any ides why gcc allows this with c++20? – Rudolfs Bundulis Oct 22 '20 at 13:57
  • gcc probably just hasn't implemented this check (yet) – Alan Birtles Oct 22 '20 at 13:59
  • @Rodolfs - Either GCC provides an extension (like Clang, but minus the warning). Or it hasn't implemented it correctly yet. Does adding `-pedantic` change GCC's behavior? – StoryTeller - Unslander Monica Oct 22 '20 at 14:00
  • @AlanBirtles btw, I actually have a limitation why I am using the `using` - in the actual sample the class inherits two traits classes which had colliding `==` operators, thus the `using` was a way to solve the ambiguity. I guess I'll stay with adding an explicit `==` operator, but I guess this shows how just removing the `using` is not a 100% solution. – Rudolfs Bundulis Oct 22 '20 at 14:01
  • @AlanBirtles nope, `-pedantic` does not affect gcc, so I guess it's just not there yet. – Rudolfs Bundulis Oct 22 '20 at 14:03
  • gcc basically implements its own set of rules around comparisons that don't quite line up with standard C++20, in order to avoid these kinds of problems. – Barry Oct 22 '20 at 14:32
  • @Barry Huh, really? Those rules would be non-conforming though, right? – cigien Oct 22 '20 at 14:42
  • @cigien Sure, but they allow perfectly functional C++17 code to compile so it's hard to really complain too much about it. – Barry Oct 22 '20 at 14:54
  • @Barry Well, for now ok. But I don't think users are going to be too happy when c++20 code starts behaving differently on different compilers. – cigien Oct 22 '20 at 15:01
  • @cigien As compared to how happy users are going to be when their code just doesn't compile in C++20? – Barry Oct 22 '20 at 17:45
  • @Barry Yes, actually. Not compiling is bad, but silently changing behavior when upgrading, or switching compilers is much worse. I fear this kind of thing is going to scare people away from upgrading, and that would be a real shame. – cigien Oct 22 '20 at 17:55
  • @cigien - Add it to the pile of reasons that make upgrading risky. Making our code build as C++17 is a hot topic in my shop currently. And the changes between C++17 and C++14 are mild in comparison. Honestly, *this* will not be the straw the breaks the camel's back for people looking into upgrading. – StoryTeller - Unslander Monica Oct 22 '20 at 18:01
  • Yes, that's fair. C++20 is a very big change, and there must be many risks that I haven't considered, this is just one that I'd noticed. Thanks for the feedback (from @Barry as well) , I appreciate it :) – cigien Oct 22 '20 at 18:05