C++ - 在模板中复制赋值运算符

C++ - Copy Assignment Operator in Template

本文关键字:复制 赋值运算符 C++      更新时间:2023-10-16

我正在尝试重载模板结构xpair中的复制赋值运算符

template <typename First, typename Second>
struct xpair {
   First first{};
   Second second{};
   xpair(){}
   xpair (const First& first, const Second& second):
           first(first), second(second) {}

   xpair& operator= (const xpair& that) {
      cout << "*this = " << *this << "      " << "that = " << that << endl;
      cout << "use operator = " << endl;
      *this = that;
      return *this;
   }
};

但是当我测试这段代码时

using commend = string;
using str_str_pair = xpair<string, string>;
using commend_pair = xpair<commend, str_str_pair>;
commend_pair cmd_pair;
str_str_pair str_pair("a", "1");
commend cmd("comment");
cmd_pair.first = cmd;
cmd_pair.second = str_pair;

它给了我无限的输出

use operator = 
*this = {,}      that = {a,1}
use operator = 
*this = {,}      that = {a,1}
use operator = 
*this = {,}      that = {a,1}
use operator = 
*this = {,}      that = {a,1}

为什么?

它给了我无限的输出
为什么?

由于已根据函数本身定义了函数,因此请参阅以下代码注释。

xpair& operator= (const xpair& that)
{
    cout << "*this = " << *this << "      " << "that = " << that << endl;
    cout << "use operator = " << endl;
    // Here you're asking for `this` (i.e., an `xpair` type) to be assigned
    // a `that` (i.e., another `xpair` type) using the `operator=` which is
    // the function currently being implemented/defined. A function calling
    // itself is recursion and there is no stopping condition so it will
    // continue infinitely.
    *this = that;
    return *this;
}

相反,您的操作应使用实例的数据成员this设置实例的数据成员that

xpair& operator= (const xpair& that)
{
    cout << "*this = " << *this << "      " << "that = " << that << endl;
    cout << "use operator = " << endl;
    first = that.first;
    second = that.second;
    return *this;
}

你的行

  *this = that;

也是一个赋值,需要两个参数。并且两者都是xpair<First,Second>,所以它再次调用同一个运算符。

您可能想做的是:

this->first = that.first;
this->second = that.second;

它调用 FirstSecond 的赋值运算符。

正如其他人所指出的,您的问题是您的operator=调用您的operator=。 这会导致无限递归。

但是,我会主张一个不同的实现:

添加这个:

template<class Self,
  class=std::enable_if_t<std::is_same<std::decay_t<Self>, xpair>{}>
>
friend auto tieme(Self&& self) {
  return std::forward_as_tuple(
    std::forward<Self>(self).first,
    std::forward<Self>(self).second
  );
}

到你们这对的身体。 enable_if_t的东西有点晦涩难懂,但它确保这个免费函数只会在真正的 xpair 上调用。

现在您的运算符 = 只是:

xpair& operator= (const xpair& that) {
  tieme(*this)=tieme(that);
  return *this;
}

这很好,因为您不必重复元素的顺序两次。

但它并不止于此。

friend bool operator<(const xpair& lhs, const xpair& rhs) {
  return tieme(lhs) < tieme(rhs);
}

同样的技术可以让你编写一堆其他运算符。 任何曾经在样板中遇到过错误的人<都会明白以上很好。

移动分配?

xpair& operator= (xpair&& that) {
  tieme(*this)=tieme(std::move(that));
  return *this;
}

交换?

friend void swap(xpair& lhs, xpair& rhs) {
  std::swap( tieme(lhs), tieme(rhs) );
}

它可以扩展 - 向tieme添加更多内容,并且它由所有其他方法自动处理。