There is a class in a very simplified form that looks like this:

class variant { public: variant (){}; template <class T> operator T () { return T(11); } operator string () { string str = "sss"; return str; } }; 

and there is a program:

 int main() { variant v1; int x1 = v1; // тут все хорошо (вызывается operator int ()) x1 = v1; // тут тоже все хорошо (вызывается так же operator int ()) variant v2; string s1 = v2; // тут все хорошо (вызывается operator string ()) s1 = v2; // тут получаю ошибку компиляции return 0; } 

I can't understand what's wrong, if I set the value of the string variable during initialization, everything is fine, but if I try to assign a value to the string variable, I get an error (with the built-in data types, everything is fine)

 error: ambiguous overload for 'operator=' (operand types are 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' and 'variant') 

what am I doing wrong? How to overload something like a string operator= (variant) ?

compiler: gcc version 5.1.0 (MinGW-W64 project)

    1 answer 1

    Everything is simple, in the string string s1 = v2; the std::string copy constructor is called, which has only one variant of the argument: const std::string& . Therefore, a conversion occurs and everything works.

    On the other hand, the string s1 = v2; calls std::string::operator= and there are already more options for the arguments, so the compiler cannot determine which type you need to convert v2 . This can be cured only by explicit conversion.


    In fact, it turns out that the same error is obtained with explicit conversion. Further there will be my speculations why this is so in order to give an answer for sure - it is necessary to study the standard.

    So, the line s1 = static_cast<std::string>(v2); generates the same error, whereas, as Harry noted, this line: s1 = static_cast<const std::string&>(v2); works as it should. My explanation is this: in the first case, you can convert something to std::string from any type whose single-argument constructor has std::string , so this line immediately generates a bunch of conversion operators and we again have ambiguity.

    On the other hand, when we try to convert to a reference to std::string , it turns out that the direct conversion wins - in indirectly, you first need to create std::string and only then you can convert to a link, that is 2 transformations, but in the first case only one is needed.

    What gives us all this? It gives us this understanding that if we write code that prevents the template conversion operator from participating in operations when converting to std::string , then our conversion code should earn:

     template<typename T> using EnableIfNotStr_t = std::enable_if_t<!std::is_convertible<T, std::string>::value>; class variant { public: variant() {}; template <class T, typename = EnableIfNotStr_t<T>> operator T () { return T(11); } operator std::string () const { std::string str = "sss"; return str; } }; //... s1 = static_cast<std::string>(v2); 
    • It is not very clear why the copy constructor is called, and not, say, the transformative constructor from const char* - αλεχολυτ
    • @ixSci Not in objection, but for understanding :) - why the correction does not pass s1 = static_cast<string>(v2); , but only s1 = static_cast<const string&>(v2); ? - Harry
    • @alexolut, because this is the initial initialization syntax (copy initialization) - ixSci
    • @ixSci, and this is then that string s2 = "char*"; ? - αλεχολυτ
    • one
      @alexolut, and this is the implicit creation of std::string and a call to the copy constructor after that - ixSci