我有一个简单的结构Wrapper
,由两个模板化赋值运算符重载区分开来:
template<typename T>
struct Wrapper {
Wrapper() {}
template <typename U>
Wrapper &operator=(const Wrapper<U> &rhs) {
cout << "1" << endl;
return *this;
}
template <typename U>
Wrapper &operator=(Wrapper<U> &rhs) {
cout << "2" << endl;
return *this;
}
};
然后我宣布a和b:
Wrapper<float> a, b;
a = b;
将b
分配给a
将使用上面的非常量模板赋值运算符重载,并显示数字“2”。
让我感到困惑的是:如果我宣布c
和d
,
Wrapper<float> c;
const Wrapper<float> d;
c = d;
并将d
分配给c
,两个赋值运算符重载都没有使用,也没有显示输出;因此调用默认的复制赋值运算符。为什么将d
分配给c
不使用提供的const重载赋值运算符?或者,为什么分配b
到a
不使用默认的复制赋值运算符?
答案 0 :(得分:18)
为什么将
d
分配给c
不使用提供的const重载赋值运算符?
仍然会生成隐式声明的复制赋值运算符,该运算符声明如下:
Wrapper& operator=(const Wrapper&);
运算符模板不会抑制隐式声明的复制赋值运算符的生成。由于参数(const-qualified Wrapper
)与此运算符(const Wrapper&
)的参数完全匹配,因此在重载决策期间选择它。
未选择运算符模板,并且没有歧义,因为 - 所有其他条件相同 - 非模板在重载决策期间比模板更好地匹配。
为什么将
b
分配给a
不使用默认的副本分配运算符?
参数(非const限定的Wrapper
)更适合采用Wrapper<U>&
的运算符模板,而不是隐式声明的复制赋值运算符(需要{{1} }}
答案 1 :(得分:6)
来自C ++ 03标准,§12.8/ 9:
用户声明的复制赋值运算符
X::operator=
是类X
的非静态非模板成员函数,其中只有一个X
类型的参数,X&
,const X&
,volatile X&
或const volatile X&
。
§12.8/ 10:
如果类定义没有显式声明一个复制赋值运算符,则会隐式声明一个。
您的operator=
是模板的事实使得不一个复制赋值运算符,因此编译器仍会生成类的隐式复制赋值运算符。