那么unique_ptr可以安全地在stl集合中使用吗?
我与unique_ptr和右值移动哲学混淆。
假设我们有两个集合:
std::vector<std::auto_ptr<int>> autoCollection; std::vector<std::unique_ptr<int>> uniqueCollection;
现在我期望下面的失败,因为没有告诉内部的algorithm是什么,也许做内部枢纽副本之类的东西,从而剥夺了auto_ptr的所有权:
std::sort(autoCollection.begin(), autoCollection.end());
我明白了 编译器正确地禁止这种情况发生。
但是,我这样做:
std::sort(uniqueCollection.begin(), uniqueCollection.end());
这个编译。 我不明白为什么。 我不认为unique_ptrs可以被复制。 这是否意味着一个枢轴值不能被采取,所以sorting效率较低? 或者这个枢轴实际上是一个移动,事实上,它与auto_ptrs的集合一样危险,应该被编译器所禁止?
我想我错过了一些关键的信息,所以我急切地等待有人给我提供啊哈! 时刻。
我认为这是哲学比技术更多的问题:)
根本的问题是移动和复制有什么区别。 我不会跳入技术/标准语言,我们只需简单地做:
- 复制:创build另一个相同的对象(或至less,应该比较相等的一个)
- 移动:取一个物体并放在另一个位置
正如您所说,可以实现复制的移动:创build一个副本到新的位置,并丢弃原来的。 但是这里有两个问题。 一个是性能,第二个是关于用于RAII的对象:哪一个应该拥有所有权?
一个合适的Move构造函数解决了两个问题:
- 很清楚哪个对象拥有所有权:新的,因为原来的将被丢弃
- 因此不需要复制指向的资源,这样可以提高效率
auto_ptr
和unique_ptr
就是一个非常好的例子。
用auto_ptr
你有一个拧的复制语义:原件和副本不相等。 您可以将其用于移动语义,但存在将丢失指向某处的对象的风险。
另一方面, 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)
所以你可以在容器中使用unique_ptr
(与auto_ptr
不同),但是一些操作是不可能的,因为它们涉及到types不支持的复制。
不幸的是,Visual Studio在标准的执行上可能相当松懈,还有一些扩展,你需要禁用以确保代码的可移植性…不要用它来检查标准:)
unique_ptr
正在被移动使用他们的移动构造函数。 unique_ptr
是可移动的,但不是CopyConstructable。
这里有一个很好的关于右值引用的文章。 如果你还没有阅读,或者感到困惑,请看看!
std::sort
只能在移动操作和不复制的情况下工作,只要每个对象在任何给定的时间只有一个活动副本。 由于原则上你可以暂时分配另一个数组,并将所有对象移动到重新sorting的位置,这是一个比在原地更弱的要求。
例如, std::vector<std::unique_ptr<T>>
超过了它的容量,它将为更大的向量分配存储空间,然后将所有对象从旧存储设备移动到新存储设备。 这不是一个就地操作,但它是完全有效的。
事实certificate,像快速sorting和堆sorting这样的sortingalgorithm实际上可以毫无困难地就地运行。 快速sorting的分区例程在内部使用std :: swap,这是所涉及的两个对象的移动操作。 当select一个数据透视表时,一个技巧就是将它与该范围内的第一个元素进行交换,这样在分区完成之前,它将永远不会移动。