0

It makes sense that the assignment expression has no ambiguity. But I don't understand why the relational expression does have.

#include <iostream>

class MyClass {
public:
    operator int() { return 0; }   
    operator unsigned int() { return 1u; }
};

int main()
{
    MyClass obj;
    std::cout << (obj == 0); // ambiguity
    unsigned int n = obj;    // no ambiguity
}
1
  • Side note : make your conversions const noexcept Commented Dec 21, 2024 at 9:57

1 Answer 1

3

unsigned int n = obj is not an assignment expression, it doesn't use any assignment, built-in or overloaded.

It is initialization and that simply initializes the object by performing overload resolution on the conversion functions in MyClass, choosing the best matching one for the type that is supposed to be initialized.


obj == 0 is however an expression and overload resolution is applied for operator== (not conversion functions!) in order to determine whether == should be interpreted as built-in comparison operator or whether it should call a operator== overload defined somewhere.

In this overload resolution the built-in operator == is represented by some imagined overload declarations

bool operator==(L, R);

for every pair of types L and R where each is either a floating point type or a promoted integral type. (And then there are further such overloads for enumeration types, pointer types, pointer-to-member types, etc., which don't matter here.)

In particular there are then candidates

bool operator==(int, int);

and

bool operator==(unsigned int, int);

Both of these are viable via user-defined conversion using the MyClass conversion functions (and neither requires any further conversion of the return value). So they have the exact same implicit conversion rank each on both of the arguments.

There is no tie-breaker in overload resolution that would disambiguate one of these candidates as better than the other and therefore overload resolution is ambiguous.

In fact, if different conversion functions are used for the implicit conversion sequence on an argument by different candidates, then the two implicit conversion sequences are always considered ambiguous, regardless of whether there is any further conversion required.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the detailed explanation. I still don't understand “neither requires any further conversion of the return value”.
@xiaokaoy If you tried to call a function void f(long); with f(obj), then both this function would be viable for both conversion functions, because they can be used to convert obj to (unsigned) int first then further to long by a standard conversion sequence. That's what I mean. Generally a user-defined conversion sequence is a standard conversion sequence followed by a user defined conversion (conversion function or constructor call) followed by a second standard conversion sequence.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.