JVApen
JVApen

Reputation: 11317

Why do conversion operators cause ambiguous overload when const-ref and value exist

I'm looking at a wrapping class, based on https://www.fluentcpp.com/category/strong-types/ The main difference is that I'm replacing the get() method with a explicit casting operator as this triggers questions during code review when used.

As you can see in the simplified code below, I have 3 overloads of the casting operator:

When writing: static_cast<int>(a), I expect the overload of const A & to int to be used. However, it seems to favor the int and the const int & overload equally. Why does it do so?

Similarly to this, it seems to allow const int &r = static_cast<const int &>(createA()); which I assume is a life-time bug. (assuming createA returns an A by value)


Simplified code at Compiler Explorer: https://gcc.godbolt.org/z/YMH9Ed

#include <utility>

struct A
{
    int v = 42;
    explicit operator int() const & { return v; } // Removing this line works
    explicit operator int() && { return std::move(v); }
    explicit operator const int &() const & { return v; }
};

int main(int, char**)
{
    A a;
    int r = static_cast<int>(a);
    return r;
}

Compilation error:

<source>:14:13: error: ambiguous conversion for static_cast from 'A' to 'int'
    int r = static_cast<int>(a);
            ^~~~~~~~~~~~~~~~~~~
<source>:6:14: note: candidate function
    explicit operator int() const & { return v; }
             ^
<source>:8:14: note: candidate function
    explicit operator const int &() const & { return v; }
             ^

Upvotes: 2

Views: 241

Answers (1)

NathanOliver
NathanOliver

Reputation: 181068

explicit operator int() const & { return v; }

and

explicit operator const int &() const & { return v; }

are equally good conversions. a is a lvalue so both functions can be called. a is also not const so both functions will have to apply a const conversion to a, so they are both still equally good. All that is left is the "return type", int or const int&, but those are both equally good to create anint from.

You need to get rid of one of conversion operators, or remove the constness from

explicit operator const int &() const & { return v; }

to turn it into

explicit operator const int &() & { return v; }

so non const lvalues give you a const reference.

Upvotes: 3

Related Questions