what cause the ambiguous overload in gcc?

Viewed 153
#include <type_traits>
#include <iostream>

template<typename T>
struct Wrapper {
    T value;
    operator T&() & { std::cout << "call const ref" << std::endl; return this->value; }
    operator const T&() const& { std::cout << "call const ref" << std::endl; return this->value; }
    operator T&&() && { std::cout << "call move" << std::endl; return std::move(this->value); }
    operator const T&() const&& = delete;
    operator T&&() & = delete;
    operator T&&() const& = delete;
};

class A {
  public:
        A& operator=(const A&) { std::cout << "use copy" << std::endl; return *this; }
        A& operator=(A&&) { std::cout << "use move" << std::endl; return *this; }
};

int main() {
    Wrapper<A> b;
    A bb;
    bb = std::move(b);
}

I compiled this code with gcc10.2, and get the following error

test.cc: In function ‘int main()’:
test.cc:24:21: error: ambiguous overload for ‘operator=’ (operand types are ‘A’ and ‘std::remove_reference<Wrapper<A>&>::type’ {aka ‘Wrapper<A>’})
   24 |     bb = std::move(b);
      |                     ^
test.cc:17:12: note: candidate: ‘A& A::operator=(const A&)’
   17 |         A& operator=(const A&) { std::cout << "use copy" << std::endl; return *this; }
      |            ^~~~~~~~
test.cc:18:12: note: candidate: ‘A& A::operator=(A&&)’
   18 |         A& operator=(A&&) { std::cout << "use move" << std::endl; return *this; }
      |            ^~~~~~~~

But I tried the same code with clang in cppinsights.io, and compiled successfully. link

So, what cause the difference between gcc and clang? And how do I change the Wrapper to fix it?

https://godbolt.org/z/37dPqafGK

1 Answers

It looks like the problem is that you have two overloads. There's the overload resolution of A::operator=, but there is also the overload resolution of the Wrapper<A> conversion operator in the conversion sequence. C++ cannot resolve multiple overloads simultaneously.

For a simpler case, imagine if A::operator= was overloaded for int and float, and Wrapper<A> defined both operator int and operator float.

This is of course only the case if there is an overload, i.e. there are multiple candidate conversion operators in Wrapper<A>. But I don't see a reason why clang should exclude 2 out of the 3 conversion operators.

I also don't seen an obvious fix. The problem itself looks ambiguous to me.

Related