2014-02-18 102 views
3

我嘗試以下的代碼:矢量::的push_back和std ::移動

#include <iostream> 

struct test{ 
    test(){} 
    test(test const &){ 
     std::cout<<__LINE__<<"\n"; 
    } 
    test(test&&){ 
     std::cout<<__LINE__<<"\n"; 
    } 
}; 

#include <vector> 
#include <utility> // std::move 
int main(){ 
    auto&& tmp = test(); 
    std::vector<test> v; 
    v.push_back(tmp); 
    std::cout<<__LINE__<<"\n"; 
    v.push_back(std::move(tmp)); 

    return 0; 
} 

的vs2013編譯器輸出:

6 //複製

9 //移動

9 //移動

的g ++以及鐺++輸出:

6 //複製

9 //移動

6 //複製

我的問題是:

  1. 是tmp測試的類型& &? tmp是一個右值嗎?

  2. 如果tmp的類型是test & &,爲什麼沒有第一個push_back使用移動構造函數?

  3. 最後一個輸出來自哪裏?爲什麼vs2013和g ++輸出不同的結果?

謝謝。

第三個問題的答案: 它來自於由andrew.punnett評論的重新分配。

+0

我已經修改了我的答案,擴大安德魯的評論。 – Potatoswatter

+0

最有效的解決方案是'v.emplace_back();',它既不需要拷貝也不需要移動。 – fredoverflow

+0

注意:'v.emplace_back()'確實會移動該對象,如果它是在調用之前構造的 – DarkWanderer

回答

4

tmptest&&的類型?

是和否。 tmptest&&類型的右值參考變量,但標識爲tmp的表達式的類型爲test,值類別爲,值爲&從來不是表達式類型的一部分。

tmp是一個右值嗎?

不需要。標識符的任何用法都是左值表達式,即使是右值引用的名稱。右值引用變量的訪問基本與左值引用變量相同;只有decltype(tmp)可以區分。 (你經常會使用到decltype((tmp))避免告訴的區別。)

如果TMP的類型是測試& &,爲什麼沒有第一的push_back使用移動構造函數?

因爲右值引用的名稱仍然是一個左值。要得到一個右值表達式,請使用move(tmp)

最後一個輸出來自哪裏?爲什麼vs2013和g ++輸出不同的結果?

Clang和GCC默認只爲vector中的一個對象騰出空間。當您添加第二個對象時,矢量存儲被重新分配,導致對象被複制。他們爲什麼不移動?因爲移動構造函數不是noexcept,所以如果拋出異常,將不可能撤銷重新分配。

至於MSVC的兩個舉措,有兩種可能性,你可以通過實驗區分 - 我沒有一個方便的副本。

  1. 默認情況下,MSVC爲矢量中的兩個對象預留了足夠的空間。第二步是從內部局部變量。
  2. MSVC忽略移動構造函數爲noexcept並要求它執行重定位的要求。這將是一個錯誤,但在這種情況下,它掩蓋了一個常見的錯誤。

如果用deque替換vector,你不會看到任何更多的副本,因爲deque不允許假設複製性。

+3

大部分都是正確的,但對構造函數的額外調用僅僅是因爲向量正在被重定位而不是使用「臨時內部變量」。例如,在創建vector之後嘗試v.reserve(2)。 –

+0

這裏是不是有一些引用崩潰的詭計,即'tmp'是什麼由RHS返回?這與整個「普遍引用」相同。 – juanchopanza

+0

因此,測試&&對測試&沒有優勢。 – cqdjyy01234

2

Visual Studio尚不支持noexcept關鍵字,並且可能不符合push_back的異常安全性。另外,額外的輸出是容量計算增長差異的結果。

#include <iostream> 
#include <vector> 
#include <utility> // std::move 

struct Except{ 
    Except(){} 
    Except(Except const &) { 
     std::cout<< "COPY\n"; 
    } 
    Except(Except&&) { 
     std::cout<< "MOVE\n"; 
    } 
}; 

struct NoExcept{ 
    NoExcept(){} 
    NoExcept(NoExcept const &) noexcept { 
     std::cout<< "COPY\n"; 
    } 
    NoExcept(NoExcept&&) noexcept { 
     std::cout<< "MOVE\n"; 
    } 
}; 

template <typename T> void Test(char const *title,int reserve = 0) { 
    auto&& tmp = T(); 
    std::cout<< title <<"\n"; 
    std::vector<T> v; 
    v.reserve(reserve); 

    std::cout<< "LVALUE REF "; 
    v.push_back(tmp); 
    std::cout<< "RVALUE REF "; 
    v.push_back(std::move(tmp)); 
    std::cout<< "---\n\n"; 
} 
int main(){ 
    Test<Except>("Except class without reserve"); 
    Test<Except>("Except class with reserve", 10); 
    Test<NoExcept>("NoExcept class without reserve"); 
    Test<NoExcept>("NoExcept class with reserve", 10); 
} 

而且鐺內結果:

Except class without reserve 
LVALUE REF COPY 
RVALUE REF MOVE 
COPY 
--- 

Except class with reserve 
LVALUE REF COPY 
RVALUE REF MOVE 
--- 

NoExcept class without reserve 
LVALUE REF COPY 
RVALUE REF MOVE 
MOVE 
--- 

NoExcept class with reserve 
LVALUE REF COPY 
RVALUE REF MOVE 
---