Operator== is ambiguous for juce::String with clang 17.0.6

This used to work just fine until clang 17.0.6 update.

	char test[20] = "test";
	if (test == juce::String("test"))
		DBG("equal!");
4>C:/dev/Nexus/Source/Synth/PluginProcessor.cpp(11,11): error : use of overloaded operator '==' is ambiguous (with operand types 'char[20]' and 'juce::String')
4>C:\dev\Nexus\modules\JUCE\modules\juce_core/text/juce_String.h(1447,29): note: candidate function
4>C:\dev\Nexus\modules\JUCE\modules\juce_core/containers/juce_Variant.h(339,15): note: candidate function

Two candidate functions are:

JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const String& string2) noexcept;
JUCE_API bool operator== (const var&, const String&);

Is this a clang bug or just the way things should actually be in C++ and in future we will need to cast the char[]?

Just for a sanity check does it work if you swap the sides of the operator i.e. juce::String("test") == test?

I think the issue here is the var class missing explicit in its constructors. Should be the best practice for writing decent c++, avoid silent and often unsafe implicit conversions

@kunitoki good point, we tend to update the non-explicit constructors when we see them, I wonder if var might be a slightly more sensitive one to touch though?

The thing that jumps out to me is that there is an explicit comparison operator for String and char* but only when char* is on the right hand side.

JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const char* string2) noexcept;

So I do wonder if we should really have those both ways around?

1 Like

Please don’t make the juce::var constructors explicit - the whole point of juce::var is to be implicit! It’s designed to replicate dynamically-typed languages like javascript’s var where you would expect to be able to pass any r-value to a variable.

C.46: By default, declare single-argument constructors explicit

Key phrase being by default. It doesn’t say always.

They give the following exception:

class Complex {
public:
    Complex(double d);   // OK: we want a conversion from d to {d, 0}
    // ...
};

Complex z = 10.7;   // unsurprising conversion

This is very similar to the situation to juce::var… juce::var v = 10.7; is an unsurprising conversion because you could replace with double or auto or whatever and the functionality would be identical.

Yeah to be honest that’s what I was thinking too. I’m hoping an issue like this can be resolved in other ways.

1 Like

The problem is when var is in the signature of similarly overloaded methods with also types that could construct a var instance, where implicit constructors are potentially getting in your way. I understand it’s an annoying change right now and hard to push for, but honestly for my classes i stick with always and i’ve never been bitten again (and code is more explict where it counts).

This is also unsurprising for me, while being explicit and not cumbersome to write, read or understand:

auto x = Complex(10.7);
1 Like

Btw to stay on topic, i think the global operator== overloads of juce String are missing some. I recall i had an issue comparing a StringRef with String or raw char pointers (string_view works in the same case).

Would an easy win be to move the comparison operator to be a member of juce::var, rather than a free function? That way it shouldn’t be included in the list of possible overloads unless you actually have a juce::var object involved?

That would work better, until you invert the lhs and rhs

Either reversing the comparison or adding the operator with the parameters reversed solves the issue.

2 Likes