In addition to an implicitly-defined copy assignment operator, if a class also defines an operator=
without an lvalue-reference object parameter, which of the operators must be selected?
Please consider the following example containing two structs, each with an overloaded copy assignment operator:
struct A {
int operator =(this const A &, const A&) { return 1; }
operator int() const { return 2; }
};
struct B {
int operator =(this volatile B &, const B&) { return 1; }
operator int() const { return 2; }
};
template<typename T>
int f(T t) {
return t = T{};
}
int main() {
return 10 * f(A{}) + f(B{});
}
The program returns two digits. The first digit is 1
if the user-defined operator =
is selected in A a; a = A{};
and it is 2
if the implicitly-defined copy assignment operator is selected. The second digit similarly shows the selection, but for B b; b = B{};
current compilers disagree on the program's returned value.
Clang returns 11
, meaning that both user-defined operators are preferred over implicitly-defined copy assignment operators.
MSVC returns 22
, always selecting the implicitly-defined copy assignment operators over user-defined operators.
And GCC is in the middle, with the return value 21
. Online demo: https://gcc.godbolt.org/z/d4hbe7cn8
Which compiler is correct here?
Clang is correct, the others aren't.
Both of your declared assignment operators are copy assignment operators per [class.copy.assign]/1:
A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one non-object parameter of type X, X&, const X&, volatile X&, or const volatile X&.
In both of your operator=
declarations the first parameter is the explicit object parameter (because of this
), i.e. an object parameter, and therefore doesn't count in the determination for copy assignment operator.
As a consequence, the compiler must not declare any copy or move assignment operator implicitly.
There is no implicit overload that could be chosen in overload resolution.
See CWG 2586.