Использование перегруженного оператора '[]' неоднозначно с оператором приведения шаблона
Следующий код хорошо компилируется в 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