Страницы

Поиск по вопросам

четверг, 7 марта 2019 г.

Непонятное поведение operator string ()

Есть некий класс, в очень упрощенном виде выглядит так:
class variant { public: variant (){};
template operator T () { return T(11); }
operator string () { string str = "sss"; return str; } };
и есть программа:
int main() { variant v1; int x1 = v1; // тут все хорошо (вызывается operator int ()) x1 = v1; // тут тоже все хорошо (вызывается так же operator int ())
variant v2; string s1 = v2; // тут все хорошо (вызывается operator string ()) s1 = v2; // тут получаю ошибку компиляции
return 0; }
немогу понять в чем дело, если задаю переменной типа string значение при инициализации то все нормально, но если пытаюсь присвоить переменной типа string значение получаю ошибку(с встроенными типами данных все хорошо):
error: ambiguous overload for 'operator=' (operand types are 'std::__cxx11::string {aka std::__cxx11::basic_string}' and 'variant')
что я делаю не так? как правильно перегрузить чтото вроде string operator= (variant)?
компилятор: gcc version 5.1.0 (MinGW-W64 project)


Ответ

Всё просто, в строке string s1 = v2; вызывается конструктор копирования std::string, у которого есть только один вариант аргумента: const std::string&. Поэтому происходит преобразование и всё работает.
С другой стороны, строка s1 = v2; вызывает std::string::operator= и тут уже вариантов аргументов куда больше, поэтому компилятор не может определить, в какой тип нужно конвертировать v2. Вылечить это можно только явным преобразованием.

На деле получается, что и с явным преобразованием получается та же ошибка. Дальше будут мои спекуляции почему так происходит, чтобы дать ответ точно — нужно штудировать стандарт.
Итак, строчка s1 = static_cast(v2); генерирует ту же самую ошибку, тогда как, как заметил Harry, такая строчка: s1 = static_cast(v2); работает как надо. Моё объяснение таково: в первом случае, конвертировать что-то в std::string можно из любого типа, одноаргументный конструктор которого имеет std::string, поэтому, эта строчка генерирует сразу пачку операторов преобразования и мы опять имеем неоднозначность.
С другой стороны, когда мы пытаемся преобразовать к ссылке на std::string, получается, что прямое преобразование побеждает — в непрямом нужно сначала создать std::string и только потом можно будет преобразовать к ссылке, т.е. 2 преобразования, а в первом случае нужно только одно.
Что нам всё это даёт? Даёт нам это понимание, что если написать код, который запретит шаблонному оператору преобразования участвовать в операциях, когда происходит конвертация в std::string, тогда наш код с преобразованием должен заработать:
template using EnableIfNotStr_t = std::enable_if_t::value>; class variant { public: variant() {};
template > operator T () { return T(11); }
operator std::string () const { std::string str = "sss"; return str; } }; //... s1 = static_cast(v2);

Комментариев нет:

Отправить комментарий