Search code examples
c++templatesc++17implicit-conversionambiguous

Why is implicit conversion not ambiguous for non-primitive types?


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.


Solution

  • According to [over.binary]/1

    Thus, for any binary operator @, x@y can be interpreted as either x.operator@(y) or operator@(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>.