临时/匿名对象与具名对象的运算符查找规则是否存在差异?
具名对象与临时对象调用基类友元运算符的差异解析
核心原因:左值引用参数的绑定限制
你的代码中,基类Base定义的友元operator+=第一个参数是非const左值引用:
friend DerivedType& operator +=(DerivedType& lhs, const integertype& rhs)
C++中,非const左值引用只能绑定到左值(具名对象),无法绑定到临时右值对象。这是导致两种调用场景差异的根本原因,而非临时对象的查找规则特殊。
未定义COMPLEX宏(mytype为double)的场景分析
1. 临时对象调用:Wrapper(2.0) += 3u
临时对象Wrapper(2.0)是右值,无法绑定到基类友元operator+=的左值引用参数,因此该友元运算符直接被排除在候选集之外。
编译器只能从Wrapper的成员运算符中选择:
Wrapper& operator+=(const Wrapper&):需要将3u通过用户定义转换(Wrapper(const unsigned&)构造)转为WrapperWrapper& operator+=(const double&):3u可通过标准转换转为double(标准转换优先级高于用户定义转换)
因此编译器选择后者,输出wrapper mytype version。
2. 具名对象调用:t_MyWrapper += 3u
具名对象t_MyWrapper是左值,可以绑定到基类友元operator+=的左值引用参数。此时候选集包括:
- 基类友元
operator+=:参数完全匹配(无需任何转换),优先级最高 Wrapper的两个成员运算符:均需要参数转换
因此编译器优先选中基类友元运算符,先输出base += version;友元函数内部调用lhs += mytype(rhs),即调用Wrapper的operator+=(const double&),再输出wrapper mytype version。
定义COMPLEX宏(mytype为std::complex<double>)的场景分析
1. 临时对象调用:Wrapper(2.0) += 3u
同样,基类友元operator+=因左值引用无法绑定临时右值,被排除在候选之外。剩下的候选:
Wrapper& operator+=(const Wrapper&):3u需通过用户定义转换转为WrapperWrapper& operator+=(const std::complex<double>&):3u需通过用户定义转换(std::complex的算术类型构造函数)转为std::complex<double>
两个候选的转换优先级相同(均为用户定义转换序列),因此编译器报歧义错误。
2. 具名对象调用
具名对象是左值,基类友元operator+=会被纳入候选,且参数完全匹配(无需转换),优先级高于需要转换的成员运算符,因此不会出现歧义,编译正常。
验证与解决方案
- 如果将基类友元
operator+=的第一个参数改为const DerivedType&(可绑定右值)或DerivedType&&(专门绑定右值),临时对象也能调用该友元运算符,消除差异。 - 将运算符移至
Wrapper类作为成员函数时,成员函数的this指针可指向右值(除非用ref-qualifier限制),因此临时对象可正常调用;若改为Wrapper的友元函数,仍需注意参数的引用类型是否支持右值绑定。 - 移除
Wrapper(const unsigned&)构造函数后,COMPLEX模式下临时对象的候选仅剩operator+=(const mytype&),消除歧义。
内容的提问来源于stack exchange,提问作者Tom_DB
相关产品推荐
相关产品推荐

