如果除了隐式定义的复制赋值运算符之外,类还定义了不带左值引用对象参数的
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{});
}
程序返回两位数。如果在
1
中选择了用户定义的 operator =
,第一位数字为 A a; a = A{};
;如果选择隐式定义的复制赋值运算符,第一位数字为 2
。第二个数字类似地显示选择,但对于 B b; b = B{};
,当前编译器不同意程序的返回值。
Clang 返回
11
意味着用户定义的运算符都优于隐式定义的复制赋值运算符。
MSVC 返回
22
始终选择隐式定义的复制赋值运算符而不是用户的复制赋值运算符。
GCC 位于中间,返回值
21
。在线演示:https://gcc.godbolt.org/z/d4hbe7cn8
这里哪个编译器是正确的?
Clang 是正确的,其他都不是。
您声明的两个赋值运算符都是复制赋值运算符 [class.copy.assign]/1:
用户声明的 copy 赋值运算符 X::operator= 是类 X 的非静态非模板成员函数,只有一个类型为 X、X&、const X&、易失性 X& 或的非对象参数const 易失性 X&.
在两个
operator=
声明中,第一个参数是显式对象参数(因为 this
),即对象参数,因此不计入复制赋值运算符的确定中。
因此,编译器不得隐式声明任何复制或移动赋值运算符。
在重载决策中没有可以选择的隐式重载。