2

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:

  • From const A & to int
  • From A && to int
  • From const A & to const int &

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; }
             ^
JVApen
  • 11,008
  • 5
  • 31
  • 67

1 Answers1

3
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.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Given your explanation, I seem to deduce that this all is based on 'overloading based on the return type'. I would argue that that would be strange, as the operator doesn't play by the same rules, cause otherwise I would have gotten a compilation error as I've created the same function twice with a different return argument.error. – JVApen Jan 12 '19 at 15:52
  • @JVApen conversion operators are special beast, the "return value" is the type you want to cast to. You can overload those because there return is basically the name of the function. In this case both functions return something that has an equal conversion to an `int` so the conversion is ambiguous. The only way to fix it is to remove one of them from the overload set. – NathanOliver Jan 12 '19 at 16:18
  • Shouldn't the exact match be preferred? – JVApen Jan 12 '19 at 16:21
  • @JVApen Both are considered an exact match in this case. – NathanOliver Jan 12 '19 at 16:34
  • Yes, I see that that is what is happening, though, reading through http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf (the parts containing `conversion operator` don't indicate that this is how it should behave – JVApen Jan 12 '19 at 17:41
  • I know this has nothing to do with the topic but I'm curious and I can't come up with a search query for this. What does the ampersand at the end of the function head mean (directly before `{`)? I've never seen that anywhere. – Timo Jan 12 '19 at 20:15
  • 2
    @timo It is a member function reference qualification. Itr says what type of object the function can be called on (rvalue `&&` ot lvalue `&`). see [this](https://stackoverflow.com/questions/8610571/what-is-rvalue-reference-for-this) and [this](https://en.cppreference.com/w/cpp/language/member_functions) – NathanOliver Jan 12 '19 at 20:24