Search code examples
c++operator-overloadingimplicit-conversionoverload-resolution

C++ operator == and implicit conversion resolution


If I have a struct A defined as:

struct A {
    const char *data;
    operator const char * () const { return data; }
    friend bool operator== (const A &s1, const char *s2)
    { return /* typical string comparison result */; }
};

And I write A{"hello"} == "test2", is A::operator== called? What in the standard states that (and why isn't the A implicitly converted to const char *?)

What about "test2" == A{"hello"}? Is the A converted in this case?

EDIT: What about if struct A also has member:

friend bool operator== (const char *s1, const A &s2)

Solution

  • When you do

    A{"hello"} == "test2"
    

    we perform overload resolution on operator==. First, we find the viable candidates ([over.match.viable]), via name lookup:

    operator==(A const&, const char*);    // yours
    operator==(const char*, const char*); // built-in
    

    Next, we determine which candidate has the best implicit conversion sequence. This is the first tiebreaker in [over.match.best]:

    Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
    (1.3) — for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that, [...]

    Both operators are Exact Match on the 2nd argument. On the first argument, your operator== is an Exact Match whereas the built-in requires a user-defined conversion. Exact Match is the best kind of conversion, and user-defined is the worst - hence, yours has the better conversion sequence and becomes the best viable function.

    In broader terms, the A isn't implicitly converted to a const char* because there's a better option where it doesn't have to be.


    When you do:

    "test2" == A{"hello"};
    

    Your candidate isn't viable - there is no implicit conversion from const char* to A const& (first argument), so the only viable candidate is the built-in comparison for const char*, which requires the user-defined conversion from A to const char*.

    If you'd like your A::operator== to be invoked, you'd have to add a new overload for operator==(const char*, A const&).