模板赋值运算符重载之谜

Posted

技术标签:

【中文标题】模板赋值运算符重载之谜【英文标题】:Template assignment operator overloading mystery 【发布时间】:2011-08-03 07:18:57 【问题描述】:

我有一个简单的结构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 使用默认的复制赋值运算符?

【问题讨论】:

【参考方案1】:

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

隐式声明的复制赋值运算符,声明如下:

Wrapper& operator=(const Wrapper&);

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

没有选择操作符模板并且没有歧义,因为 - 在所有其他条件相同的情况下 - 在重载决策期间,非模板比模板更匹配。

为什么将b 赋值给a 不使用默认的复制赋值运算符?

与隐式声明的复制赋值运算符(采用const Wrapper&lt;U&gt;&amp;)相比,参数(非 const 限定 Wrapper)更适合采用 Wrapper&lt;U&gt;&amp; 的运算符模板。

【讨论】:

+1 for 运算符模板不会抑制隐式声明的复制赋值运算符的生成【参考方案2】:

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

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

第 12.8/10 条:

如果类定义没有显式声明复制赋值运算符,则隐式声明一个。

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

【讨论】:

如果不是复制赋值运算符,那它是什么?那么你应该怎么做——只做一个 CopyFrom 函数模板或类似的东西? @DavidDoria :这是一个赋值运算符,而不是 copy 赋值运算符。

以上是关于模板赋值运算符重载之谜的主要内容,如果未能解决你的问题,请参考以下文章

模板类的重载赋值运算符

如何为从C++中的模板继承的类重载赋值运算符

模板类复制内部类问题中的模板赋值运算符

C++中赋值运算操作符和=重载有啥区别?

c++中为啥赋值运算符重载返回类型是引用

带下标运算符的重载赋值运算符