模板赋值运算符重载神秘

use*_*370 7 c++ templates assignment-operator

我有一个简单的结构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;
  }
};
Run Code Online (Sandbox Code Playgroud)

然后我宣布a和b:

Wrapper<float> a, b;
a = b;
Run Code Online (Sandbox Code Playgroud)

分配ba将使用非const模板赋值操作过载从上方和数字"2"被显示.

令我困惑的是:如果我宣布cd,

Wrapper<float> c;
const Wrapper<float> d;
c = d;
Run Code Online (Sandbox Code Playgroud)

并且分配dc,既没有使用两个赋值运算符重载,也没有显示输出; 因此调用默认的复制赋值运算符.为什么分配dc未使用const重载赋值操作符提供?或者相反,为什么分配ba 使用默认的拷贝赋值运算符?

Jam*_*lis 18

为什么分配dc未使用const重载赋值操作符提供?

仍然会生成隐式声明的复制赋值运算符,该运算符声明如下:

Wrapper& operator=(const Wrapper&);
Run Code Online (Sandbox Code Playgroud)

运算符模板不会抑制隐式声明的复制赋值运算符的生成.由于参数(const-qualified Wrapper)与此运算符(const Wrapper&)的参数完全匹配,因此在重载决策期间选择它.

未选择运算符模板且没有歧义,因为 - 所有其他条件相同 - 非模板在重载决策期间比模板更好地匹配.

为什么分配ba不使用默认的拷贝赋值运算符?

参数(非const限定Wrapper)是一个更好的匹配运算符模板,它采用一个Wrapper<U>&比隐式声明的复制赋值运算符(它需要一个const Wrapper<U>&.


ild*_*arn 6

从C++ 03标准,§12.8/ 9:

用户声明的拷贝赋值运算符X::operator=是一个非静态的非模板类的成员函数X与类型的只有一个参数X,X&,const X&,volatile X&const volatile X&.

§12.8/ 10:

如果类定义未显式声明复制赋值运算符,则会隐式声明一个.

operator=是模板的事实使它不是复制赋值运算符,因此类的隐式复制赋值运算符仍由编译器生成.