2010-05-20 89 views
42

我與unique_ptr和右值移動哲學混淆。那麼unique_ptr可以在stl集合中安全使用嗎?

比方說,我們有兩個類別:

std::vector<std::auto_ptr<int>> autoCollection; 
std::vector<std::unique_ptr<int>> uniqueCollection; 

現在我希望下面的失敗,因爲沒有告訴什麼算法內部做,也許使內部樞副本之類的,這樣翻錄離開auto_ptr的所有權:

std::sort(autoCollection.begin(), autoCollection.end()); 

我明白了。編譯器正確地禁止了這種情況發生。

但後來我這樣做:

std::sort(uniqueCollection.begin(), uniqueCollection.end()); 

這編譯。我不明白爲什麼。我不認爲unique_ptrs可以被複制。這是否意味着無法採用樞軸值,因此排序效率較低?或者這個樞軸實際上是一個移動,事實上它與auto_ptrs的集合一樣危險,應該被編譯器禁止?

我想我錯過了一些關鍵信息,所以我熱切地等待有人給我提供aha!時刻。

+1

其實編譯器*應該*抱怨'std :: vector > autoCollection;'因爲COAPS(自動指針的容器)不允許在任何描述中。 – 2010-05-20 18:55:45

+0

使用VS2010。我甚至沒有在/ W4發出警告。 – DanDan 2010-05-20 19:42:04

+1

或收到auto_ptr折舊的警告。 – DanDan 2010-05-20 20:01:34

回答

50

我認爲它更比哲學TECHNIC的問題:)

根本的問題是,什麼是移動和複製的區別。我不會跳進技術/ standardista語言,讓我們這樣做只是:

  • 複製:創建另一個相同的對象(或至少一個應該比較相等)
  • 移動:取一個對象,並把它在另一個位置

正如您所說,可以實現移動的複製條件:創建一個副本到新的位置並丟棄原件。但是這裏有兩個問題。一個是性能,第二個是關於用於RAII的對象:這兩個中的哪一個應該擁有所有權?

一個適當的轉移構造解決了2個問題:

  • 很清楚哪一個對象具有所有權:新的一個,因爲原來的會被丟棄
  • 因此,不需要複製的資源指出, ,這允許更高的效率

auto_ptrunique_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),而是因爲它們涉及該類型不支持複製多項業務將是不可能的。

不幸的是,Visual Studio在標準的實施中可能相當鬆懈,並且還有一些擴展需要禁用以確保代碼的可移植性...不要用它來檢查標準:)

+0

謝謝你,很好的回答和例子:) – DanDan 2010-05-21 19:29:20

+3

在你的評論「罰款,因爲使用移動構造函數」你的意思是「移動任務或交換」?另外,爲了完整起見,下面是'std :: move'和'std :: make_move_iterator'完成'不可能'的方法:http://coliru.stacked-crooked.com/a/6b032d70a9ed6f5c – sehe 2013-09-19 13:18:12

+0

@sehe:你是對,我的意思是移動分配操作員。 – 2013-09-19 13:39:00

11

unique_ptr正在被移動使用他們的移動構造函數。 unique_ptr是可移動的,但不是CopyConstructable。

有一篇關於右值引用here的文章。如果你還沒有讀過它們,或者感到困惑,請看看!

+0

嘿,感謝這篇偉大的文章!雖然在第7頁有點瘋狂,但是我學到了很多東西 但是我原來的問題仍然存在。爲什麼移動安全但拷貝不是?不是unique_ptr的移動與auto_ptr的副本相同?如果對象的移動構造函數使用std :: move,即所有權的轉移,是不是auto_ptr副本的默認行爲? – DanDan 2010-05-20 20:20:06

+1

是的,但auto_ptr來自C++ 0x之前,並且明確表示不是允許在容器中使用 另一方面,由於unique_ptr可以安全地移動,因此您(和編譯器)可以放心,不會違反所有權。另外,請記住,STL的排序算法是未指定的,並且在C++ 0x中只需要移動東西即可。但是可以在原地進行快速排序,因此不需要任何副本。 – rlbond 2010-05-20 22:10:10

7

std::sort只能在移動操作和不復制的情況下工作,只要每個對象在任何給定時間只有一個活動副本即可。由於原則上你可以暫時分配另一個數組,並將所有對象移動到重新排序的位置,這是一個比在原地更弱的要求。例如

例如std::vector<std::unique_ptr<T>>超過其容量,它爲較大的向量分配存儲空間,然後將所有對象從舊存儲設備移動到新存儲設備。這不是一個就地操作,但它是完全有效的。

事實證明,快速排序和堆排序等排序算法實際上可以毫無困難地就地工作。快速排序的分區例程在內部使用std :: swap,這將作爲所涉及對象的移動操作。當選擇一個數據透視表時,一個技巧就是將它與該範圍內的第一個元素進行交換,這樣在分區完成之前,它將永遠不會移動。

相關問題