如何将 unique_ptr 从 vector<unique_ptr<Foo>> 中移出?

5
我想移动一个unique_ptr<Foo>出一个 vector<unique_ptr<Foo>>。考虑下面的代码:

#include <vector>
#include <memory>
#include <iostream>

using namespace std;

class Foo {
public:
  int x;
  Foo(int x): x(x) {};
  ~Foo() {
    cout << "Destroy of id: " << x << "\n";
    x = -1;
  };
};

int main(int argc, char *argv[]) {
  auto foos = vector<unique_ptr<Foo>>();
  foos.push_back(unique_ptr<Foo>(new Foo(100)));
  foos.push_back(unique_ptr<Foo>(new Foo(101)));
  foos.push_back(unique_ptr<Foo>(new Foo(102)));

  // Print all
  cout << "Vector size: " << foos.size() << "\n";
  for (auto i = foos.begin(); i != foos.end(); ++i) {
    cout << (*i)->x << "\n";
  }

  // Move Foo(100) out of the vector
  {
    auto local = move(foos.at(0));
    cout << "Removed element: " << local->x << "\n";
  }

  // Print all! Fine right?
  cout << "Vector size: " << foos.size() << "\n";
  for (auto i = foos.begin(); i != foos.end(); ++i) {
    cout << (*i)->x << "\n";
  }

  return 0;
}

我预计这会带来以下结果:
Vector size: 3
100
101
102
Removed element: 100
Destroy of id: 100
Vector size: 2
101
102

但是实际上,我得到了这个结果:
Vector size: 3
100
101
102
Removed element: 100
Destroy of id: 100
Vector size: 3
Segmentation fault: 11

为什么我的向量大小仍然是3,为什么会出现分段错误?我该如何得到我想要的结果?

4
向量并没有损坏。在对其进行解引用之前,您可以检查unique_ptr。但是,您选择不这样做。 - juanchopanza
1
@juanchopanza,我已经清楚地发布了我想要的期望输出。你还想要什么? - Doug
根据你所说的,你并不想移动任何东西,你只是想要复制。 - AndyG
2
我希望你能提出一个明确、简洁的问题。 - juanchopanza
1
你有一个(智能)指针的向量。移动后,其中一个指针不再有效(变为nullptr)。尝试对其进行解引用是未定义行为。这与在vector<int*> v;中执行v[0] = nullptr并尝试解引用*v[0]没有区别。行为是一致的,std::move没有问题,向量也没有“损坏”,它应该是这样的。你唯一的机会是重新分配向量,但在这种情况下,你可以直接复制。 - vsoftco
2
@Doug,换一种方式提问可能会有所帮助。您已经成功地将指针移出向量。因此,您不应再询问如何移动指针。相反,您应该询问如何从向量中删除旧条目。 - Aaron McDaid
1个回答

11
让我们将您的问题简化为:
vector<unique_ptr<Foo>> foos;
foos.push_back(unique_ptr<Foo>(new Foo(100)));
auto local = std::move(foos[0]);
std::cout << foos[0]->x << '\n';

当您通过移动foos[0]创建local后,foos[0]不再拥有指针的所有权。它是空的。对它进行解引用将变成未定义的行为,在您的情况下会表现为段错误。此时vector完全“完好无损”,它包含一个空的unique_ptr,并且等效于以下状态:

vector<unique_ptr<Foo>> foos(1);

在解引用 unique_ptr 指针之前,您应该简单地检查它是否拥有指针:

if (foos[0]) {
    // we wouldn't get here
    std::cout << foos[0]->x << '\n';
}

或者,由于您希望强制执行您的vector仅包含有效指针的不变量,在移动操作的一部分中,您应该只需erase该元素:

auto local = std::move(foos[0]);
foos.erase(foos.begin());
// now foos is empty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接