那么unique_ptr可以在stl集合中安全使用吗?

Dan*_*Dan 43 c++ auto-ptr unique-ptr c++11

我对unique_ptr和rvalue move哲学很困惑.

假设我们有两个集合:

std::vector<std::auto_ptr<int>> autoCollection;
std::vector<std::unique_ptr<int>> uniqueCollection;
Run Code Online (Sandbox Code Playgroud)

现在我希望以下内容失败,因为没有人知道算法在内部做了什么,也许还在制作内部数据透视副本等,从而剥夺了auto_ptr的所有权:

std::sort(autoCollection.begin(), autoCollection.end());
Run Code Online (Sandbox Code Playgroud)

我明白了 并且编译器正确地不允许这种情况发生.

但后来我这样做:

std::sort(uniqueCollection.begin(), uniqueCollection.end());
Run Code Online (Sandbox Code Playgroud)

这编译.我不明白为什么.我不认为unique_ptrs可以复制.这是否意味着不能采用枢轴值,因此排序效率较低?或者这个转轴实际上是一个移动,实际上和auto_ptrs的集合一样危险,编译器应该不允许这样做?

我想我错过了一些重要的信息,所以我急切等待有人给我提供啊哈!时刻.

Mat*_* M. 52

我认为这更像是一个哲学问题而不是技术问题:)

根本问题是Move和Copy之间的区别是什么.我不会跳进技术/标准语言,让我们简单地说:

  • 复制:创建另一个相同的对象(或至少,一个应该比较相同的对象)
  • 移动:取一个物体并将其放在另一个位置

正如您所说,可以在复制方面实现Move:在新位置创建副本并丢弃原始位置.但是那里有两个问题.一个是性能,第二个是用于RAII的对象:两者中哪一个应该拥有所有权?

一个合适的Move构造函数解决了两个问题:

  • 很明显哪个对象拥有所有权:新对象,因为原始对象将被丢弃
  • 因此,不必复制指向的资源,这样可以提高效率

auto_ptrunique_ptr就是其中一个很好的例证.

随着auto_ptr你有一个拧复制语义:原和拷贝不比较平等的.您可以将它用于Move语义,但是您可能会丢失指向某处的对象.

另一方面,unique_ptr正是如此:它保证了资源的唯一所有者,从而避免了复制和随之而来的不可避免的删除问题.并且在编译时也保证无副本.因此,只要您不尝试复制初始化,它就适用于容器.

typedef std::unique_ptr<int> unique_t;
typedef std::vector< unique_t > vector_t;

vector_t vec1;                           // fine
vector_t vec2(5, unique_t(new Foo));     // Error (Copy)
vector_t vec3(vec1.begin(), vec1.end()); // Error (Copy)
vector_t vec3(make_move_iterator(vec1.begin()), make_move_iterator(vec1.end()));
    // Courtesy of sehe

std::sort(vec1.begin(), vec1.end()); // fine, because using Move Assignment Operator

std::copy(vec1.begin(), vec1.end(), std::back_inserter(vec2)); // Error (copy)
Run Code Online (Sandbox Code Playgroud)

所以你可以unique_ptr在容器中使用(不像auto_ptr),但是许多操作是不可能的,因为它们涉及类型不支持的复制.

不幸的是,Visual Studio在执行标准方面可能相当松懈,并且还有许多扩展,您需要禁用它们以确保代码的可移植性...不要用它来检查标准:)

  • 在您的评论中"很好,因为使用Move构造函数"的意思是"移动赋值还是`交换`"?另外,为了完整起见,使用`std :: move`和`std :: make_move_iterator`可以完成'不可能'的工作:http://coliru.stacked-crooked.com/a/6b032d70a9ed6f5c (3认同)

rlb*_*ond 12

unique_ptrs的正使用他们的移动构造函数移动.unique_ptr是Movable,但不是CopyConstructable.

有一个关于右值引用一个伟大的文章在这里.如果你还没有读过它们,或者感到困惑,那就看看吧!


yon*_*nil 7

std::sort只要在任何给定时间只有每个对象的一个​​实时副本,它只能用于移动操作而不能复制.这比在原地工作要求更弱,因为原则上您可以临时分配另一个数组并在重新排序时移动所有对象.

例如,std::vector<std::unique_ptr<T>>超过其容量,它为更大的向量分配存储,然后将所有对象从旧存储移动到新存储.这不是就地操作,但它完全有效.

事实证明,快速排序和堆排序等排序算法实际上可以毫无困难地就地工作.quick-sort的分区例程在内部使用std :: swap,它被视为所涉及对象的移动操作.选择枢轴时,一个技巧是将其与范围中的第一个元素交换,这样在分区完成之前永远不会移动它.