I have two classes, one of which, say, represents a string, and the other can be converted to a string:
class A {
public:
A() {}
A(const A&) {}
A(const char*) {}
A& operator=(const A&) { return *this; }
A& operator=(const char*) { return *this; }
char* c;
};
class B {
public:
operator const A&() const {
return a;
}
operator const char*() const {
return a.c;
}
A a;
};
Now, if I do
B x;
A y = x;
It triggers copy constructor, which compiles fine. But if I do
A y;
y = x;
It complains about ambiguous assignment, and can't choose between =(A&)
and =(char*)
. Why the difference?
For example: a = 10; b = 20; ch = 'y'; “+=”: This operator is combination of '+' and '=' operators. This operator first adds the current value of the variable on left to the value on the right and then assigns the result to the variable on the left.
An assignment operator is the operator used to assign a new value to a variable, property, event or indexer element in C# programming language. Assignment operators can also be used for logical operations such as bitwise logical operations or operations on integral operands and Boolean operands.
Assignment (=) The simple assignment operator ( = ) is used to assign a value to a variable. The assignment operation evaluates to the assigned value. Chaining the assignment operator is possible in order to assign a single value to multiple variables.
There is a difference between initialization and assignment.
In initialization, that is:
A y = x;
The actual call depends on the type of x
. If it is the same type of y
, then it will be like:
A y(x);
If not, as in your example, it will be like:
A y(static_cast<const A&>(x));
And that compiles fine, because there is no ambiguity any more.
In the assignment there is no such special case, so no automatic resolution of the ambiguity.
It is worth noting that:
A y(x);
is also ambiguous in your code.
There is §13.3.1.4/(1.2), only appertaining to (copy-)initialization of objects of class type, that specifies how candidate conversion functions for your first case are found:
Under the conditions specified in 8.5, as part of a copy-initialization of an object of class type, a user-defined conversion can be invoked to convert an initializer expression to the type of the object being initialized. Overload resolution is used to select the user-defined conversion to be invoked. […] Assuming that “cv1
T
” is the type of the object being initialized, withT
a class type, the candidate functions are selected as follows:
The converting constructors (12.3.1) of
T
are candidate functions.When the type of the initializer expression is a class type “cv
S
”, the non-explicit conversion functions ofS
and its base classes are considered. When initializing a temporary to be bound to the first parameter of a constructor where the parameter is of type “reference to possibly cv-qualifiedT
” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2T
”, explicit conversion functions are also considered. Those that are not hidden withinS
and yield a type whose cv-unqualified version is the same type asT
or is a derived class thereof are candidate functions. […] Conversion functions that return “reference toX
” return lvalues or xvalues, depending on the type of reference, of type X and are therefore considered to yieldX
for this process of selecting candidate functions.
I.e. operator const char*
is, though being considered, not included in the candidate set, since const char*
is clearly not similar to A
in any respect. However, in your second snippet, operator=
is called as an ordinary member function, which is why this restriction doesn't apply anymore; Once both conversion functions are in the candidate set, overload resolution will clearly result in an ambiguity.
Note that for direct-initialization, the above rule doesn't apply either.
B x;
A y(x);
Is ill-formed.
A more general form of this result is that there can never be two user-defined conversions in one conversion sequence during overload resolution. Consider §13.3.3.1/4:
However, if the target is
- the first parameter of a constructor or […]
and the constructor […] is a candidate by
- 13.3.1.3, when the argument is the temporary in the second step of a class copy-initialization, or
- 13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases),
user-defined conversion sequences are not considered. [Note: These rules prevent more than one user-defined conversion from being applied during overload resolution, thereby avoiding infinite recursion. — end note ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With