Ambiguous string :: operator = type call with implicit conversion to int and string

Given the following program:

#include <iostream> #include <string> using namespace std; struct GenericType{ operator string(){ return "Hello World"; } operator int(){ return 111; } operator double(){ return 123.4; } }; int main(){ int i = GenericType(); string s = GenericType(); double d = GenericType(); cout << i << s << d << endl; i = GenericType(); s = GenericType(); //This is the troublesome line d = GenericType(); cout << i << s << d << endl; } 

It compiles in Visual Studio 11, but not clang or gcc. He is having problems because he wants to implicitly convert from GenericType to int to char , but can also return string , and so there is uncertainty ( operator=(char) and operator=(string) both correspond to GenericType ).

The copy constructor is just good.

My question is: how to resolve this ambiguity without changing the contents of the main? What do I need to do to change GenericType to handle this situation?

+8
c ++ string implicit-conversion conversion-operator overload-resolution
source share
3 answers

I believe gcc and clang are correct.

There are two operator= overloads in the game:

 string& operator=(string const& str); // (1) string& operator=(char ch); // (2) 

Both of these operator= overloads require a custom conversion from your argument of type GenericType . (1) requires the use of conversion to string . (2) requires the use of conversion to int , and then the standard conversion to char .

The important thing is that both overloads require a custom conversion. To determine whether one of these transformations is better than the other, we can look for rules for resolving overloads, in particular the following rule from C ++ 11 ยง13.3.3.2 / 3 (reformatted for clarity):

A user transform sequence U1 is a better transform sequence than another user transform sequence U2 if

  • they contain the same user-defined transform function or constructor or aggregate initialization and

  • the second standard conversion sequence U1 better than the second standard conversion sequence U2 .

Note that a connects the two parts of the rule, so both parts must be satisfied. The first part of the rule is not executed: two user-defined conversion sequences use different user-defined conversion functions.

Therefore, no conversion is better, and the challenge is ambiguous.

[I have no good suggestion to fix the problem without changing the definition of main() . Implicit conversions are usually not a good idea; they are sometimes very useful, but more often they can cause overload ambiguity or other strange overload behavior.]

A gcc error report was found in which this problem was described and resolved by design: the compiler incorrectly diagnoses an ambiguous operator overload.

+9
source share

I believe the GCC is wrong. In the book "C ++ Programming Language" Bjarne Straustup has a whole chapter on operator overloading. Section 11.4.1 in the first paragraph states the following:

"Assigning a value of type V to an object of class X is legal if there is an assignment operator X :: operator = (Z), so that V is Z or there is a single conversion of V to Z. Initialization is considered equivalent."

In your example, GCC takes "string s = GenericType ();" but rejects "s = GenericType ();" so it does not explicitly consider assignment in the same way as initialization. This was my first clue that something was wrong with the GCC.

GCC reports 3 candidates for converting GenericType to a string in an assignment, all in basic_string.h. One of them is the correct conversion, one of the messages that it reports is invalid, and the third causes ambiguity. This is an operator overload in basic_string.h that causes ambiguity:

 /** * @brief Set value to string of length 1. * @param c Source character. * * Assigning to a character makes this string length 1 and * (*this)[0] == @a c. */ basic_string& operator=(_CharT __c) { this->assign(1, __c); return *this; } 

This is not a valid conversion because it accepts an operand that does not match the type of object that was passed to it. In no case should you assign a char attempt, so this conversion should not be a candidate for all the much smaller ones, which causes ambiguity. GCC seems to mix the type of the template with the type of the operand in its member.

EDIT: I did not know that assigning an integer to a string was actually legal, since the integer can be converted to a char, which can be assigned to a string (although the string cannot be initialized to char!). GenericType defines a conversion to int, which makes this member a valid candidate. However, I still argue that this is not a valid conversion, the reason is that using this conversion will result in two custom implicit conversions for assignment, first from GenericType to int, and then from int to string. As stated in the same book 11.4.1, "only one level of user implicit conversion is legal."

+2
source share

My question is: how to resolve this ambiguity without changing the contents of the main?

Create your own class called string that does not have an ambiguous operator= , and then without using std .

Obviously, this is not a very good solution, but it works, and main does not need to be changed.

I do not think that you can get the behavior you want in any other way.

+1
source share

All Articles