Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous assignment operator

Tags:

c++

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?

like image 518
riv Avatar asked May 27 '15 10:05

riv


People also ask

What are examples of assignment operators?

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.

What is assignment operator in OOP?

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.

Which character is the assignment operator?

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.


2 Answers

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.

like image 116
rodrigo Avatar answered Oct 02 '22 21:10

rodrigo


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, with T 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 of S 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-qualified T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T”, explicit conversion functions are also considered. Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions. […] Conversion functions that return “reference to X” return lvalues or xvalues, depending on the type of reference, of type X and are therefore considered to yield X 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 ]

like image 39
Columbo Avatar answered Oct 02 '22 21:10

Columbo