【问题标题】:Copy constructor chosen over move constructor when returning non-static local object返回非静态本地对象时选择复制构造函数而不是移动构造函数
【发布时间】:2020-11-03 17:38:23
【问题描述】:

我曾经假设一个类的移动构造函数优先于其复制构造函数,但在下面的代码中,似乎选择了复制构造函数,即使对象应该是可移动的。

你知道为什么下面的代码在foo()返回vector<B> B时选择复制构造函数吗?

#include <iostream>
#include <vector>

using namespace std;

class B {
public:
  int var_;

  B(int var) : var_(var)
  {
    cout << "I'm normal" << endl;
  }

  B(const B& other)
  {
    cout << "I'm copy constructor" << endl;
  }

  B(B&& other)
  {
    cout << "I'm move constructor" << endl;
  }
};

vector<B> foo()
{
  vector<B> b;

  b.push_back(1);
  b.push_back(2);

  return b;
}

int main()
{
  vector<B> b {foo()};
}

结果如下图。

$ g++ main.cpp
$ a.out
I'm normal
I'm move constructor
I'm normal
I'm move constructor
I'm copy constructor

奇怪的是,如果我删除foo() 中的一行,则会选择移动构造函数:

vector<B> foo()
{
  vector<B> b;

  b.push_back(1);

  return b;
}

现在结果如下:

$ g++ main.cpp
$ a.out
I'm normal
I'm move constructor

【问题讨论】:

  • 当向量被移动时,它的元素完全不受影响。
  • 第二个push_back 导致向量被调整大小和重新分配;然后将现有数据从旧数据复制到新数据。在您的main 中添加b.reserve(3);,您将看不到副本。
  • 向量在插入时复制其内容以展开(与返回无关)。这样做是因为您的移动构造函数可能会抛出。所以它做动作是不安全的。声明B(B&amp;&amp; other) noexcept
  • 如果你在return b;之前打印了一些东西,你会注意到在向量返回之前所有的移动和复制都已经完成了。
  • 谢谢大家,我学到了很多!

标签: c++ vector g++ move-semantics


【解决方案1】:

涉及两件事:向量重新分配和重新分配时的机制选择。

首先,重新分配发生在这里:

vector<B> foo()
{
  vector<B> b;

  b.push_back(1);
  std::cout << "Vector capacity: " << b.capacity() << " Vector size: " << b.size() << "\n";
  b.push_back(2); //capacity() == size(), reallocation is needed

  return b;
}

当超出current_capacity 时,大多数向量实现都会使容量2*current_capacity 符合标准要求的摊销常数复杂性。


现在,如果标记为noexcept,编译器只能选择移动构造函数进行重新分配。为了使向量使用移动构造函数,声明如下:

B(B&& other) noexcept
{
//
}

您可以通过预先预留空间来完全移除重新分配:

vector<B> foo()
{
  vector<B> b;
  b.reserve(2);
  b.push_back(1);
  b.push_back(2);

  return b;
}

或者一次性初始化向量:

vector<B> foo()
{
  return vector<B>{1, 2};
}

【讨论】:

  • 非常感谢 ^^ ,到目前为止我的理解是矢量的 RVO 不是这里的问题,我看到和混淆的日志是由 B 类和错误的错误实现引起的- 在 foo() 中使用它,如果我错了请告诉我
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-07
  • 2022-11-21
  • 2021-12-25
  • 2023-03-20
  • 1970-01-01
相关资源
最近更新 更多