Использование перегруженного оператора '[]' неоднозначно с оператором приведения шаблона

Следующий код хорошо компилируется в gcc 7.3.0, но не компилируется с clang 6.0.0.

#include <string>

struct X {
    X() : x(10) {}
    int operator[](std::string str) { return x + str[0]; }
    template <typename T> operator T() { return x; } // (1) fails only in clang
    //operator int() { return x; } // (2) fails both in gcc and clang
private:
    int x;
};

int main() {
    X x;
    int y = 20;
    int z = int(x);
    return x["abc"];
}

Я использовал команду clang++ 1.cpp -std=c++98 с указанием разных стандартных версий. Я пробовал С++ 98,11,14,17,2a. Во всех случаях ошибка одинакова. Сообщение об ошибке в Clang выглядит следующим образом:

1.cpp:14:13: error: use of overloaded operator '[]' is ambiguous (with operand types 'X' and 'const char [4]')
    return x["abc"];
           ~^~~~~~
1.cpp:5:9: note: candidate function
    int operator[](std::string str) { return x + str[0]; }
        ^
1.cpp:14:13: note: built-in candidate operator[](long, const char *)
    return x["abc"];
            ^
1.cpp:14:13: note: built-in candidate operator[](long, const volatile char *)
1 error generated.

Какой компилятор правильно следует стандарту в этой ситуации? Это действительный код?

Описание проблемы можно найти здесь, но это о ситуации (2). Я заинтересован в случае (1).

Ответы

Ответ 1

GCC не прав. Случай шаблона не должен иметь никакого значения.

[over.match.best]/1 говорит:

Определите ICSi (F) следующим образом:

  • ...

  • пусть ICSi (F) обозначает последовательность неявного преобразования, которая преобразует i-й аргумент в списке в тип i-го параметра жизнеспособной функции F. [over.best.ics] определяет последовательности неявного преобразования и [over. ics.rank] определяет, что значит для одной неявной последовательности преобразования быть лучшей последовательностью преобразования или худшей последовательностью преобразования, чем другой.

С учетом этих определений жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов я ICSi (F1) не хуже последовательности преобразования, чем ICSi (F2), и...

Два жизнеспособных кандидата

int         operator[](X&,             std::string); // F1
const char& operator[](std::ptrdiff_t, const char*); // F2

... и ICS1 (F1) (X → X&) лучше, чем ICS1 (F2) (X → std::ptrdiff_t), независимо от того, является ли X → std::ptrdiff_t функцией преобразования шаблона, но ICS2 (F1) (const char[4] → std::string) хуже, чем ICS2 (F2) (const char[4] → const char*). Так что ни одна функция не лучше, чем другая, что приводит к неоднозначности.

Это было сообщено как ошибка GCC.

Ответ 2

Проблема в том, что на каждом пути есть одно преобразование:

  • сначала из "abc" в std::string а затем вызов operator[].
  • второй от x до std::ptrdiff_t а затем operator[] для std::ptrdiff_t и const char*.

Поэтому решение состоит в том, чтобы сделать оператор преобразования explicit:

int operator[](const std::string& str) { return x + str[0]; }
template <typename T>
explicit operator T() { return x; } // (1) fails only in clang