2016-04-14 12 views
3

"C++ Concurrency in Action"の本を読んでいて、スレッドセーフなデータ構造(スタックなど)で例外の安全性を理解しようとしています。競合状態を避けるために、著者の状態は、popは両方の操作を行う必要があります - しかし、ポップ、スタックから項目を返す:shared_ptrと例外の安全性を返す

ポップ()関数は、ポップ値を返すだけでなく、からそれを削除するように定義された

場合スタックが変更された後にのみポップされた値が呼び出し元に返されますが、データをコピーして呼び出し元に返すプロセスで例外がスローされる可能性があります。ここで

解決策が提案されている。この場合

std::shared_ptr<T> pop() 
{ 
    std::lock_guard<std::mutex> lock(m); 
    if(data.empty()) throw runtime_error("empty"); 
    std::shared_ptr<T> const res(std::make_shared<T>(data.top())); 
    data.pop(); 
    return res; 
} 

我々はmake_sharedライン上コピーコンストラクタの呼び出しを得ました。コピーコンストラクタが例外をスローすると、スタックはまだ変更されておらず、良好です。

私はそれがこのスニペットから多くをどのように異なるかを見ていないしかし:

T pop2() { 
    std::lock_guard<std::mutex> lock(m); 
    if(data.empty()) throw runtime_error("empty"); 
    auto res = data.top(); 
    data.pop(); 
    return res; 
} 

ここでは、リターンにdata.top()ラインでコピーコンストラクタ呼び出しと移動コンストラクタを持っています。繰り返しますが、コピーコンストラクタが例外をスローした場合、スタックはまだ変更されていないので、良いことです。 MoveContructorは例外をスローすることは想定されていません。

何か不足していますか? shared_ptrを返すことのメリットはTです。

+1

移動コンストラクタはスローすることができます。 – Simple

+0

本の私のPDFコピーには、 'throw runtime_error(" empty ")'の代わりに 'throw empty_stack()'があります。本の 'empty_stack'クラスは' std :: exception'から直接継承しています。あなたの質問の 'runtime_error'が' std :: runtime_error'である場合、 'throw runtime_error(" empty ");' [他の例外も同様に投げるかもしれません](https://stackoverflow.com/questions/36106747)/how-to-construct-a-stdexcept-exception-without-throwing)が含まれます。 – jotik

+0

ええ、私も 'empty_stack'を持っています。この例では 'runtime_exception'と置き換えました。 – lstipakov

答えて

1

Tが移動できない場合、コードによって複数のコピー構成が行われる可能性があります。この場合、shared_ptrを使用してヒープにコピーを割り当てる方が効率的です。

また、Tが移動可能な場合でも、あなたのコードでは複数の移動が可能です(潜在的に)move-constructions。 shared_ptrの移動構築および/またはコピー構築は、可能なタイプの移動またはコピー構築に比べて比較的安価であることが知られている。T

実際の走行距離は、正確なタイプ、コンパイラ、および使用環境およびハードウェアによって異なる場合があります。

+0

したがって、 'T'が移動可能であり、' constructor'が例外をスローしないと仮定した場合、 'shared_ptr'を返すだけの利点は、' move constructor'呼び出しを保存することであり、例外安全性に関係しません。 – lstipakov

+1

はい、私はそう思います。より正確に言えば、 'T 'の移動構成を1つ保存しますが、' shared_ptr 'の移動を追加します。 2番目の違いは、 'std :: make_shared'は**ヒープ**に値を割り当て、' pop2() 'では** stack **の値を構築して返します。これにはパフォーマンス上の影響もありますメモリの局所性に関する。 – jotik

1

move-constructorは(一般的に)スロ​​ーすることがあります。 std::shared_ptr<T>にはnoexcept移動コンストラクタがあります。

したがって、最初のケースではreturn res;は投げられませんが、2番目のケースではreturn res;が投げられる可能性があります(そしてdata.pop()は既に呼び出されています)。

関連する問題