拷贝赋值运算符重载决议:用户定义与隐式定义的优先级争议
C++显式重载
operator=与隐式拷贝赋值运算符的匹配问题 当类中存在用户定义的、带显式this参数的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{}); }
行为差异说明
程序返回值为两位数字,每一位对应一个结构体的运算符选择结果:
- 第一位:若
A a; a = A{};选择用户定义的operator=则为1,选择隐式拷贝赋值运算符则为2 - 第二位:若
B b; b = B{};选择用户定义的operator=则为1,选择隐式拷贝赋值运算符则为2
不同编译器的返回值存在分歧:
- Clang返回
11:优先选择用户定义的运算符 - MSVC返回
22:始终选择隐式定义的运算符 - GCC返回
21:A选择隐式版本,B选择用户定义版本
标准规则与正确行为分析
1. 隐式拷贝赋值运算符的生成条件
根据C++标准,只有当类未声明任何拷贝赋值运算符时,编译器才会生成隐式拷贝赋值运算符。而拷贝赋值运算符的判定标准为:
- 非模板、非静态成员函数
- 除显式
this参数外,仅接受一个类型为X/X&/const X&/volatile X&/const volatile X&的参数(X为类本身) - 返回类型为
X&
示例中的两个operator=返回类型均为int,不满足拷贝赋值运算符的返回类型要求,因此它们不属于拷贝赋值运算符,编译器仍会为A和B生成隐式拷贝赋值运算符,签名分别为:
A& operator=(const A&);(this参数为A&)B& operator=(const B&);(this参数为B&)
2. 重载决议的匹配优先级
赋值表达式t = T{}的重载决议会在隐式拷贝赋值运算符和用户定义的operator=中选择最优候选,核心判断依据是参数匹配度:
- 对于
struct A:
赋值表达式中的t是A类型的非const左值。隐式版本的this参数为A&(非const),与t的类型精确匹配;用户定义版本的this参数为const A&,需要将非const左值转换为const引用(限定转换)。根据标准规则,精确匹配的优先级高于限定转换,因此会选择隐式拷贝赋值运算符,最终通过operator int()转换为2。 - 对于
struct B:
赋值表达式中的t是B类型的非volatile左值。隐式版本的this参数为B&(非volatile),与t精确匹配;用户定义版本的this参数为volatile B&,需要将非volatile左值转换为volatile引用(限定转换)。同样,精确匹配优先级更高,因此会选择隐式拷贝赋值运算符,最终转换为2。
综上,程序的正确返回值应为22,MSVC的行为符合C++标准规定。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

