Given a simple class template with multiple implicit conversion functions (non-explicit constructor and conversion operator), as in the following example:
template<class T>
class Foo
{
private:
T m_value;
public:
Foo();
Foo(const T& value):
m_value(value)
{
}
operator T() const {
return m_value;
}
bool operator==(const Foo<T>& other) const {
return m_value == other.m_value;
}
};
struct Bar
{
bool m;
bool operator==(const Bar& other) const {
return false;
}
};
int main(int argc, char *argv[])
{
Foo<bool> a (true);
bool b = false;
if(a == b) {
// This is ambiguous
}
Foo<int> c (1);
int d = 2;
if(c == d) {
// This is ambiguous
}
Foo<Bar> e (Bar{true});
Bar f = {false};
if(e == f) {
// This is not ambiguous. Why?
}
}
The comparison operators involving primitive types (bool
, int
) are ambiguous, as expected - the compiler does not know whether it should use the conversion operator to convert the left-hand template class instance to a primitive type or use the conversion constructor to convert the right-hand primitive type to the expected class template instance.
However, the last comparison, involving a simple struct
, is not ambiguous. Why? Which conversion function will be used?
Tested with compiler msvc 15.9.7.
According to [over.binary]/1
Thus, for any binary operator
@
,x@y
can be interpreted as eitherx.operator@(y)
oroperator@(x,y)
.
According to this rule, in the case of e == f
, the compiler can only interpret it as e.operator==(f)
, not as f.operator==(e)
. So there is no ambiguity; the operator==
you defined as a member of Bar
is simply not a candidate for overload resolution.
In the case of a == b
and c == d
, the built-in candidate operator==(int, int)
(see [over.built]/13) competes with the operator==
defined as a member of Foo<T>
.