模板赋值操作符重载之谜

Template assignment operator overloading mystery

本文关键字:重载 赋值操作符      更新时间:2023-10-16

我有一个简单的结构体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"。

让我困惑的是:如果我声明cd

Wrapper<float> c;
const Wrapper<float> d;
c = d;

并将d赋值给c,两个赋值操作符都没有重载,也没有输出;因此将调用默认的复制赋值操作符。为什么将d赋值给c不使用提供的const重载赋值操作符?或者,为什么将b赋值给a 而不是使用默认的复制赋值操作符?

为什么将d赋值给c不使用提供的const重载赋值操作符?

仍然生成隐式声明的复制赋值操作符,声明如下:

Wrapper& operator=(const Wrapper&);

操作符模板不抑制隐式声明的复制赋值操作符的生成。由于实参(const限定的Wrapper)与该操作符的形参(const Wrapper&)完全匹配,因此在重载解析期间选择它。

操作符模板没有被选中,并且没有歧义,因为在其他条件相同的情况下,在重载解析过程中,非模板比模板更适合匹配。

为什么将b赋值给a不使用默认的拷贝赋值操作符?

实参(非const限定的Wrapper)比隐式声明的复制赋值操作符(接受const Wrapper<U>&)更适合接受Wrapper<U>&的操作符模板。

来自c++ 03标准,§12.8/9:

用户声明的复制赋值运算符X::operator=X类的非静态非模板成员函数,只有一个形参类型为XX&const X&volatile X&const volatile X&

和§12.8/10:

如果类定义没有显式声明复制赋值操作符,则隐式声明。

事实上,你的operator=是一个模板,使得不是一个复制赋值操作符,所以类的隐式复制赋值操作符仍然是由编译器生成的。