2017-01-04 13 views
2

具体的には、ダイレクトリスト初期化cppreference.com(3))です。std :: make_shared/std :: make_uniqueがリストの初期化を使用しない理由はありますか?

両方std::make_shared均一初期機能はC++ 11に導入されました。したがって、ヒープ上にオブジェクトを割り当てる際に集約初期化を使用することができます。new Foo{1, "2", 3.0f}。これは、集約、ポッドなどのコンストラクタを持たないオブジェクトを直接初期化するのに最適です。

カジュアル構造体を関数内に効率的に宣言するなどの実際のシナリオは、ラムダは、私の経験では、非常に一般的になった:

void foo() 
{ 
    struct LambdaArgs 
    { 
     std::string arg1; 
     std::string arg2; 
     std::string arg3; 
    }; 

    auto args = std::make_shared<LambdaArgs>(LambdaArgs{"1", "2", "3"}); 

    auto lambda = [args] { 
     /// ... 
    }; 

    /// Use lambda 
    /// ... 
} 

ここauto args = std::make_shared<LambdaArgs>("1", "2", "3");はいいことwhouldが、std::make_sharedは通常として実装されているので、仕事に行くされていません。

template<typename T, typename... Args> 
std::shared_ptr<T> make_shared(Args && ...args) 
{ 
    return std::shared_ptr<T>(new T(std::forward<Args>(args)...)); 
} 

だから我々はauto args = std::make_shared<LambdaArgs>(LambdaArgs{"1", "2", "3"});と付き合っている。

std::make_sharedで解決されるはずの問題は、コンストラクタのないオブジェクトに対しても引き続き発生します。また、この回避策は、美的ではないだけでなく効率も悪くなります。

これは別の見落としであるか、この選択肢を守る理由がいくつかあります。具体的には、どのような落とし穴は、リストの初期化ソリューションにすることができますか? std::make_uniqueは、C++ 14に後で導入されました。なぜ同じパターンに従っていますか?

+1

あなたの質問に答えることはできませんが、回避策:カジュアルな構造体の代わりにタプルを使用する場合は、適切なコンストラクタが提供され、さらに軽い: 'LambdaArgs = std :: tuple '; – Frank

+0

'std :: make_shared'が解決している問題は、オブジェクトが作成された後でshared_ptrが所有権を取得する前に例外がスローされた場合、すべてが正しくクリーンアップされていることを確認することです。それは軽微な見落としのように見えます。 – Kevin

+0

これは興味深いかもしれません:http://stackoverflow.com/questions/24234480/stdmake-shared-with-stdinitializer-list – marcinj

答えて

3

具体的には、どのような落とし穴をリスト初期化ソリューションに含めることができますか?

リスト初期化の一般的な落とし穴のすべて。

たとえば、非initializer_listコンストラクタの非表示。 make_shared<vector<int>>(5, 2)は何をしますか?あなたの答えが「5 intの配列を構築している」場合、それは絶対に正しいです... make_sharedがリスト初期化を使用していない限り。それはあなたの瞬間を変えるからです。

間接初期化関数のすべてがコンストラクタ構文を使用しているので、これを突然変更すると、既存のコードが破損することに注意してください。だから、あなたはそれを気取らずに変えて、世界が働き続けることを期待することはできません。この場合に特有の

プラス1以上:狭く問題:

struct Agg 
{ 
    char c; 
    int i; 
}; 

あなたはこの集計を初期化するAgg a{5, 1020};を行うことができます。しかし、決してmake_shared<Agg>(5, 1020)を行うことはできませんでした。どうして?コンパイラは、リテラル5をデータ損失なしでcharに変換できることを保証できるためです。しかし、このような間接的な初期化を使用すると、5という文字列は、テンプレートによって推定され、intと推定されます。そしてコンパイラのいずれもintをデータの損失なしでcharに変換できることを保証できません。これは「ナロー変換」と呼ばれ、リストの初期化では明示的に禁止されています。

5を明示的にcharに変換する必要があります。

標準ライブラリには、LWG 2089の問題があります。技術的にはこの問題はallocator::constructについて述べていますが、それはmake_Xのような間接的な初期化関数と、any/optional/variantのC++ 17のインプレースコンストラクタの両方に等しく適用されます。

なぜ同じパターンに従っていますか?

根本的にも予想外にも異なる動作を持つ2つの異なる機能を持つことは良いことではないため、同じパターンに従います。

+0

BTWカジュアル構造は、* aggregate *の代わりではなく、狭いコンテキスト(*関数の本体*)内での1回限りの使用のために宣言された構造体の名前です。 – GreenScape

1

std :: make_sharedで解決されるはずの問題は、コンストラクタなしのオブジェクトに対しても引き続き発生します。

いいえ、問題は解決しません。主な問題make_sharedは、オブジェクトが割り当てられている間にメモリリークが発生する可能性があり、所有権はスマートポインタによって取得されます。また、制御ブロックの1つの余分な割り当てを削除することもできます。

はい、直接初期化を使用できないのは不便ですが、これは決してmake_sharedの宣言された目標ではありません。

+1

make_sharedはメモリリークとは関係ありません。 make_sharedが提供するものは、参照カウントとオブジェクトの間の参照の局所性を保証し、逆参照時に余分なキャッシュミスを回避する可能性があります。 – Frank

+1

@Frank、おそらくあなたはここで見て自分を教育することができます:http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared – SergeyA

+1

私は訂正します。参照ありがとう!編集:あなたのリンクは、余分な割り当てを避けることが主な理由だと言って、メモリリークを避けることは素晴らしい利益です、私はその愚かな気がしません。 – Frank

関連する問題