user9132451
user9132451

Reputation: 59

Use of overloaded operator[] is ambiguous

Simplified code as below:

#include <string>
#include <string_view>

struct object{
  operator std::string(){return "";}
}

struct foo{
  foo operator[](std::string_view s){
    return foo{};
  }
  
  template <typename T>
  operator T(){
    return object{};
  }
};

int main(){
  foo f;
  std::string s = f["a"];
}

clang gives an error:

error: use of overloaded oeprator '[]' is ambiguous (with oeprand types 'foo' and 'const char*')
note: candidate function foo operator[](std::string_view s)
note: built-in candidate operator[](long, const char*)
note: built-in candidate operator[](long, const volatile char*)

but gcc compiles above code successfully.

clang version is 12.0.1, gcc is 7.5.0

I'm confused, which compiler is right?

Upvotes: 4

Views: 395

Answers (1)

Aleksandr Medvedev
Aleksandr Medvedev

Reputation: 8968

template <typename T>
operator T(){
    return object{};
}

I think clang is correct here, since this snippet makes the foo class convertible to any type, and viable template functions are supposed to be instantiated before overload resolution comes into play:

Before overload resolution begins, the functions selected by name lookup and template argument deduction are combined to form the set of candidate functions

As you can see, the overload struggles with the following arguments: (long, const char*), so it has to be expression similar to this 3["a"] which is perfectly legal as per cppreference:

expr1 [ expr2 ]

For the built-in operator, one of the expressions (either expr1 or expr2) must be a glvalue of type “array of T” or a prvalue of type “pointer to T”, while the other expression (expr2 or expr1, respectively) must be a prvalue of unscoped enumeration or integral type. The result of this expression has the type T


However it also gives a clue how to differentiate between built-in subscript and custom overloads:

expr1 [ { expr, ... } ]

The form with brace-enclosed list inside the square brackets is only used to call an overloaded operator[].

So you should be good if the said example is written like this:

int main(){
    foo f;
    std::string s = f[{"a"}];
    return 0;
}

Or convert the subscript to string explicitly, so the compiler doesn't confuse it with built-in operator for arrays:

int main(){
    using namespace std::string_literals;
    
    foo f;
    std::string s = f["a"s];
    return 0;
}

Upvotes: 2

Related Questions