2013-03-31 22 views
8

下面給出類X(不是明確定義的其他特殊成員函數是不相關的該實驗):爲什麼當容量超過時,resize()會導致向量內容的複製而不是移動?

struct X 
{ 
    X() { } 
    X(int) { } 
    X(X const&) { std::cout << "X(X const&)" << std::endl; } 
    X(X&&) { std::cout << "X(X&&)" << std::endl; } 
}; 

下面的程序創建X類型的對象的一個​​矢量並調整它,以便它的容量是突破和再分配強制:

#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<X> v(5); 
    v.resize(v.capacity() + 1); 
} 

由於類X提供了一個移動的構造,我希望向量的以前的內容是移動入T他重新分配後新的存儲空間。非常令人驚訝,that does not seem to be the case,輸出我得到的是:

X(X const&) 
X(X const&) 
X(X const&) 
X(X const&) 
X(X const&) 

爲什麼?

+0

還要注意的是_Microsoft_不尊重這個規則,使這個代碼_VisualÇ++編譯_(或_clang_上_Windows_)將調用移動的構造,從而可以讓你在'的std ::向量中斷(空)的元素'如果調整大小失敗。 – Denis

回答

17

段落的C++ 11標準規定(關於vector<>類模板的resize()成員函數)的23.3.6.3/14:

備註:如果一個異常被拋出其他比非CopyInsertable T的移動構造函數沒有影響

換言之,這意味着,對於X(這是CopyInsertable),resize()提供strong guarantee:它成功或離開向量不變的狀態。

爲了滿足這個保證,實現通常採用copy-and-swap idiom:如果X的拷貝構造函數拋出,我們還沒有改變原始向量的內容,所以承諾保持不變。

但是,如果矢量的以前的內容是移動到新的存儲,而不是被複制和移動構造函數拋出,那麼我們就必須不可逆轉地改變載體的原創內容。

因此,實施將使用的X拷貝構造函數向量的內容安全地轉移到新的存儲除非此舉構造被稱爲不丟,在這種情況下,它是安全的,從以前的元素移動。

一個小的改變,以X的移動構造函數的定義(標記爲noexcept),事實上,the output of the program is now the expected one

struct X 
{ 
    X() { } 
    X(int) { } 
    X(X const&) { std::cout << "X(X const&)" << std::endl; } 
    X(X&&) noexcept { std::cout << "X(X&&)" << std::endl; } 
//   ^^^^^^^^ 
}; 
+1

發生了什麼 - 博客文章的問題? :-) –

+0

@KerrekSB:有人告訴我自我回答問題都OK,我想這一次將是有趣的:) –

+0

@AndyProwl - 我看到別人努力做到這一點,他們被大頭短棒連擊... –

5

想想異常保證:如果有重新分配過程中的異常,矢量必須保持不變。這隻能通過複製元素並保留舊集來保證,直到整個副本成功完成。

只有當您知道移動構造函數不會拋出時,才能將元素安全地移動到新位置。要實現這一點,請聲明移動構造函數noexcept

相關問題