【问题标题】:Why copy constructor is called instead of move constructor with an rvalue initializer?为什么调用复制构造函数而不是使用右值初始值设定项的移动构造函数?
【发布时间】:2021-04-03 06:49:05
【问题描述】:

这是SimpleString的定义。它同时实现了复制构造函数和移动构造函数。

struct SimpleString {
  SimpleString(size_t max_size)
      : max_size{ max_size }
      , length{}  {
    if(max_size == 0) {
      throw std::runtime_error{ "Max size must be at least 1." };
    }
    buffer = new char[max_size];
    buffer[0] = 0;
  }
  ~SimpleString() {
    delete[] buffer;
  }
  SimpleString(const SimpleString& other)
      : max_size{ other.max_size }
      , buffer{ new char[other.max_size] }
      , length{ other.length } {
        puts("copy constructor");
    std::strncpy(buffer, other.buffer, max_size);
  }
  SimpleString(SimpleString&& other) noexcept
      : max_size(other.max_size)
      , buffer(other.buffer)
      , length(other.length) {
        puts("move constructor");
    other.length = 0;
    other.buffer = nullptr;
    other.max_size = 0;
  }

  void print(const char* tag) const {
    printf("%s: %s", tag, buffer);
  }
  bool append_line(const char* x) {
    const auto x_len = strlen(x);
    if(x_len + length + 2 > max_size)
      return false;
    std::strncpy(buffer + length, x, max_size - length);
    length += x_len;
    buffer[length++] = '\n';
    buffer[length] = 0;
    return true;
  }

  size_t max_size;
  char* buffer;
  size_t length;
};

我实现了+ 运算符。

SimpleString operator+(const SimpleString& str_1, const SimpleString& str_2) noexcept {
    SimpleString str_3{str_1}; // copy
    str_3.append_line(str_2.buffer);
    str_3.buffer[str_3.length-1]='\0';
    return str_3;
}

这个函数是否返回一个右值?

在 c++ 入门(第 5 期)中:

返回非引用类型的函数以及算术、关系、按位和后缀递增/递减运算符都会产生右值。

所以我认为operator+ 返回一个右值。

int main() {
  SimpleString a{ 50 };
  a.append_line("We apologise for the");
  SimpleString b{ 50 };
  b.append_line("Last message");
    auto c = a+b; // copy constructor
    c.print("c");
}

我以为它会调用移动构造函数并打印move constructor,因为a+b 是一个右值。

但是输出是:

copy constructor
c: We apologise for the
Last message

为什么不调用移动构造函数?

【问题讨论】:

  • 复制构造函数在你标记// copy的地方被调用。返回时应用的 RVO,没有调用任何构造函数。
  • 不要忘记五法则。您还需要实现复制和移动赋值运算符,否则稍后会遇到问题。
  • @S.M.这就是答案,为什么要发表评论?

标签: c++


【解决方案1】:

因为复制省略,或者返回值优化 (RVO) here!

这是因为类类型和返回的类型是相同的类型。

因此,编译器不是在内存中的某个位置构造对象str_3,然后复制/移动它,而是(更理想地)就地构造它!

我认为您可以通过指定来强制使用移动构造函数:

return std::move(str_3)

我过去也有同样的问题,但由于某种原因他们关闭了它...... here!

【讨论】: