2013-08-06 10 views
20

私はいくつかのC++ 11の機能をいくつかテストしています。 私はr値の参照と移動コンストラクタに出くわしました。C++ 11 rvalue referenceもまたコピーコンストラクタを呼び出します

私は私の最初の移動のコンストラクタを実装し、ここにある:

#include <iostream> 
#include <vector> 
using namespace std; 

class TestClass{ 

public: 
    TestClass(int s): 
     size(s), arr(new int[s]){ 
    } 
    ~TestClass(){ 
     if (arr) 
      delete arr; 
    } 
    // copy constructor 
    TestClass(const TestClass& other): 
      size(other.size), arr(new int[other.size]){ 
     std::copy(other.arr, other.arr + other.size, arr); 
    } 

    // move constructor 
    TestClass(TestClass&& other){ 
     arr=other.arr; 
     size=other.size; 

     other.arr=nullptr; 
     other.size=0; 
    } 

private: 
    int size; 
    int * arr; 
}; 

int main(){ 
    vector<TestClass> vec; 

    clock_t start=clock(); 
    for(int i=0;i<500000;i++){ 
     vec.push_back(TestClass(1000)); 
    } 
    clock_t stop=clock(); 
    cout<<stop-start<<endl; 

    return 0; 
} 

コードが正常に動作します。とにかく、コピーコンストラクタ内にstd :: coutを置くと、呼び出されることに気付きました!そして、多くの時間..(コンストラクタを500000回移動し、コンストラクタを524287回コピーする)。

私がコードからコピーコンストラクタをコメントアウトすると、プログラム全体が非常に高速になり、今回は移動コンストラクタが1024287回呼び出されます。

ヒント?

+1

どのコンパイラを使用していますか? – doctorlove

+0

http://coliru.stacked-crooked.com/view?id=0b61fede4fd9aef84b124760919e8ca8-4c5ca02fa47b506419b4501a6b65fb4f –

+0

私はgcc 4.8.1を使用しています –

答えて

30

は、あなたの移動のコンストラクタにnoexceptを置く:

TestClass(TestClass&& other) noexcept { 

推敲を:私はこの1つピエールを与えるつもりだったが、残念ながらcppreferenceソースはほぼ正確です。 C++で

vector<T>::push_back(T) 

は "強い例外保証を" 持っています。つまり、push_backが例外をスローすると、ベクトルはpush_backの呼び出し前と同じ状態になります。

この保証は、移動コンストラクタが例外をスローした場合に問題になります。

とき vector再割り当て、それだろう 新しい古いバッファから移動 の要素などがあります。しかし、これらの移動のいずれかが例外をスローすると(最初のバッファ以外に)、古いバッファが変更された状態になり、新しいバッファにはそれが想定されるすべてのバッファがまだ格納されません。 vectorは、元のバッファを元の状態に戻すことはできません。そのため、要素を元に戻す必要があるため、これらのの移動も失敗する可能性があります。だから、ルールはC++ 11のために敷設された

Tは古いものからバッファから要素を移動するために使用することができnoexcept移動コンストラクタを持っている場合
  1. それ以外の場合は、Tにコピーコンストラクタがある場合は、代わりにそのコンストラクタが使用されます。

    それ以外の場合(アクセス可能なコピーコンストラクタがない場合)は、移動コンストラクタが使用されますが、この場合、強力な例外安全性の保証はもうありません。

明確化:ルール2の「コピーコンストラクタは、」const T&、されていないものウィニーの一つは、いわゆるT&コピーコンストラクタを取るコンストラクタを意味します。:-)

+1

あなたはあなたの答えをもっと詳しく説明できますか?noexceptは何をしますか? – Alon

+0

Sod all on Visual studio :-(「エラーC3646: 'noexcept':不明なオーバーライド指定子」 – doctorlove

+0

@Alon http://accu.org/index。php/conferences/accu_conference_2013/accu2013_sessions#move_noexcept_and_push_back_and_how_it_relates_to_each_other – doctorlove

14

使用noexceptあなたの移動のコンストラクタに:

TestClass(TestClass&& other) noexcept { ... } 

noexceptこのような定数式のないnoexcept(true)と同等です。

コンパイラは非投げの機能上の特定の最適化を可能にするだけでなく、特定の発現が例外をスローするように宣言されている場合、コンパイル時にチェックすることができnoexcept演算子を、有効にするには、この情報を使用することができます。

たとえば、std :: vectorなどのコンテナは、要素の移動コンストラクタがnoexceptの場合は要素を移動し、そうでない場合はコピーします。

出典:http://en.cppreference.com/w/cpp/language/noexcept_spec

NB:これはC++ 11機能です。特定のコンパイラでまだ実装されていない可能性があります...(例:Visual Studio 2012

-1

別の質問です。ムーブコンストラクタにおいて、

// move constructor 
TestClass(TestClass&& other){ 
    arr=other.arr; 
    size=other.size; 

    other.arr=nullptr; 
    other.size=0; 
} 

それは

ARR = STDすべきではない:移動(other.arr)。

サイズ= std:move(other.size);

(でも、それらは右辺値参照として宣言された)(そのような関数のパラメータなど)は、すべての名前付きの値が常に左辺値として評価しているという事実

ため?

0

コピーコンストラクタは、std::vector内のすべての予約メモリが使用されたときに呼び出されます。要素を追加する前にstd::vector::reserve()メソッドを呼び出す必要があります。

vector<TestClass> vec; 
vec.reserve(500000); 
関連する問題