Obscure Xcode warning in JUCE code with C++20 enabled

Steps to reproduce (tested with current JUCE develop):

  1. Generate a default project with Projucer

  2. Open it in Xcode

  3. Change C++ standard to C++20

  4. Add the following code:

     juce::WeakReference <juce::Component> weakRef1, weakRef2;
     if (weakRef1 == weakRef2)
     { /* whatever */ }
    

Compilation triggers the following warning on the line with the weakRef1 == weakRef2 test:

Main.cpp:37:22: ISO C++20 considers use of overloaded operator '==' (with operand types 'juce::WeakReference<juce::Component>' and 'juce::WeakReference<juce::Component>') to be ambiguous despite there being a unique best viable function.

juce_WeakReference.h:119:10: Ambiguity is between a regular call to this operator and a call with the argument order reversed

I’m not entirely sure what it is complaining about…

Follow up: today I learned, that starting from C++20 the compiler can look up operator overloads also considering the operands in reverse order, see: Comparisons in C++20

Still I can’t conjure what’s needed to avoid that warning though

To me it looks like that class would want to compare like

if ( refA == refB.get())

But I’m sleep deprived…

It is an interesting aspect of c++ 20 I’ll be looking more into tomorrow!

Ah, yes!
Thanks to your observation, I realized that the fact that the existence of only

WeakReference::operator==(ObjectType*)

means that when comparing two WeakReferences (as opposed to one WeakReference and one raw pointer), the compiler could call it either as

weakRef1 == weakRef2.get()

or otherwise

weakRef2 == weakRef1.get()

And it is warning about this ambiguity because in one case .get() is called on weakRef1 and in the other it is called on weakRef2, breaking the simmetry.

The warning goes away if I add a better candidate for the comparison between two WeakReference instances, in the form of this code added to the WeakReference class definition:

bool operator== (const WeakReference& other) const = default;

Now I hope JUCE developers will pick this up soon.

1 Like

Caveat: setting comparison operators to “= default” is a C++20 feature, thus the code above should be put inside appropriate guards to be backwards compatible with earlier standards.

Follow up: after some further consideration, I’ve decided for the time being to add the following methods to my copy of JUCE to silence the warning:

bool operator== (const WeakReference& other) const noexcept { return get() == other.get(); }
bool operator!= (const WeakReference& other) const noexcept { return get() != other.get(); }

Even if I would have liked the idea of adding operator==() = default better (it’s more C++20-y), this solution has a few practical advantages that better suit my current situation:

  1. This code is backwards compatible with C++17 (I have some projects that share the same copy of JUCE, that are not ready to be bumped to C++20 yet)

  2. Looking at the implementation of the other (in)equality operators with raw pointers, I’m confident this is the intended behavior when comparing two WeakReferences.
    I’m not equally confident that the same results are obtained using the = default alternative.
    In the latter case, the implementation is left to the compiler which does it by comparing the instance members, and that means delegating the comparison to the comparison between WeakPointer::SharedRef instances. For now they are ReferenceCountedObjectPtr under the hood, which seem to do the right thing, but who knows what may silently change in the future if I merge some more JUCE code down the line?

Thanks for reporting. This should be resolved on develop:

1 Like