C ++ 17でstd :: make_uniqueを使用する理由


96

私が理解している限りでは、C ++ 14が導入さstd::make_uniqueれたのは、パラメーターの評価順序が指定されなかった結果、これが安全ではなかったためです。

f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A

(説明:評価が最初にrawポインターにメモリを割り当て、次に呼び出しをg()行い、std::unique_ptr構築前に例外がスローされた場合、メモリがリークします。)

呼び出しstd::make_uniqueは呼び出し順序を制限する方法であり、これにより物事が安全になります。

f(std::make_unique<MyClass>(param), g());             // Syntax B

それ以来、C ++ 17があまりにも構文Aの安全を作り、評価順序を明らかにしたので、ここで私の質問です:まだ使用する理由であるstd::make_unique以上std::unique_ptrの++ 17 Cでコンストラクタを?いくつか例を挙げていただけますか?

今のところ、私が想像できる唯一の理由は、それがMyClass一度だけタイプできることです(あなたがでポリモーフィズムに依存する必要がないと仮定するとstd::unique_ptr<Base>(new Derived(param)))。しかし、それはかなり弱い理由のようです、特にstd::make_uniquestd::unique_ptrコンストラクターを使用している削除者を指定できないです。

そして明確にするために、私はstd::make_unique標準ライブラリから削除することを支持することはしません(少なくとも後方互換性のためにそれを維持することは推奨しません)。std::unique_ptr


4
しかし、それはかなり弱い理由のようです ->なぜそれが弱い理由なのですか?タイプのコードの重複を効果的に減らします。削除機能については、カスタム削除機能を使用する頻度はどれくらいstd::unique_ptrですか?それは反対の議論ではありませんmake_unique
llllllllll '20 / 12/18

2
それが弱い理由だと私は言います。そもそもそれがなければstd::make_unique、それをSTLに追加するのに十分な理由ではないと思います。特に、コンストラクターを使用するよりも表現力が弱い構文の場合はそうではありません
永遠の

1
make_uniqueを使用してc ++ 14で作成されたプログラムがある場合、関数をstlから削除する必要はありません。または、下位互換性が必要な場合。
Serge、

2
@Sergeそれは良い点ですが、それは私の質問の目的以外に少しです。私はそれをより明確にするために編集を行います
Eternal

1
@Eternalは、C ++標準ライブラリをSTLとして参照することをやめてください。STLは正しくないため、混乱を招きます。stackoverflow.com/questions/5205491/…を
Marandil 2018

回答:


73

主な理由が削除されたのはあなたの言うとおりです。まだあり、新しい使用していないガイドラインをし、それが少ないタイピングの理由(タイプを繰り返すか、単語を使用する必要はありませんであることnew)。確かにそれらは強力な議論ではありませんが、私は見ないことが本当に好きですnew、私はコードに表示されがです。

また、一貫性を忘れないでください。あなたは絶対に使用make_sharedする必要がありますので、使用make_uniqueは自然であり、パターンに適合します。次に、構文Aを書き換える必要がある場所(またはその逆)に変更std::make_unique<MyClass>(param)するのは簡単std::make_shared<MyClass>(param)です。


40
@reggaeguitar表示された場合new、停止して考える必要があります。このポインタはどれくらいの期間存続するでしょうか。正しく処理しましたか?例外がある場合、すべてが正しくクリーンアップされていますか?私はそれらの質問をしないで自分の時間を無駄にしたくないのでnew、を使用しない場合、それらの質問をする必要はありません。
NathanOliver '20

5
プロジェクトのすべてのソースファイルに対してgrepを実行し、単一のを見つけられなかったとしますnew。これは素晴らしいことではないでしょうか?
セバスチャンマッハ

5
「新しいものを使用しない」ガイドラインの主な利点はシンプルであることです。そのため、経験の浅い開発者が作業している可能性のある開発者に簡単にガイドラインを提供できます。最初は気づかなかったが、それ自体に価値がある
永遠

@NathanOliverあなたは実際にはしません 絶対に使用する必要がありますstd::make_shared-割り当てられたオブジェクトが大きいとたくさんのがある場合を想像しstd::weak_ptr、それを指して-sを:それは、オブジェクトがすぐに最後の共有などとして削除されるようにする方が良いだろうポインタは破棄され、小さな共有領域だけで生きます。
Dev Null

1
@NathanOliverあなたはそうしません。私が話しているのは、オブジェクトを格納するために使用されたメモリが最後のものがなくなるまで解放できない(すべての-sがそれを指しているとしても)std::make_shared stackoverflow.com/a/20895705/8414561の欠点ですその結果、オブジェクト自体)はすでに破棄されています)。std::weak_ptrstd::shared_ptr
Dev Null

50

make_unique区別TからT[]T[N]unique_ptr(new ...)いません。

あなたは簡単にして、ポインタ渡して未定義の動作を得ることができますnew[]し編をunique_ptr<T>、またはされたポインタ渡すことnewにエドunique_ptr<T[]>


さらに悪いことです。それだけではなく、完全に不可能です。
Deduplicator

21

その理由は、重複のない短いコードにするためです。比較する

f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());

あなたが保存しMyClassnew中かっこ。makeptrと比較して1文字多くかかります。


2
さて、質問で述べたようにMyClass、についての言及が1つしかないため、入力が少ないことがわかりますが、それを使用するより強い理由があるかどうか疑問に思いました
Eternal

2
多くの場合、控除ガイドは<MyClass>最初のバリアントの一部を排除するのに役立ちます。
Antの

9
それは他の回答のコメントですでに述べられていますが、c ++ 17はコンストラクターのテンプレート型の控除を導入しましたが、std::unique_ptr許可されていない場合はそうです。それは区別することと関係がstd::unique_ptr<T>ありますstd::unique_ptr<T[]>
永遠の

19

のすべての使用はnew、生涯の正確さのためにさらに注意深く監査する必要があります。削除されますか?1回だけ?

のすべての使用はmake_unique、これらの追加の特性のためではありません。所有オブジェクトが「正しい」存続期間を持っている限り、それは再帰的に一意のポインタに「正しい」を持たせます。

さて、1からまでのunique_ptr<Foo>(new Foo())すべての点で同じであるというのは事実です。単純な「ソースコードをgrepしてすべてを使用して監査する」ことが必要です。make_unique<Foo>()new


1実際には、一般的なケースでは嘘です。完全な転送は完璧ではありません{}。デフォルトのinit、配列はすべて例外です。


技術的にunique_ptr<Foo>(new Foo)まったく同じではありませんmake_unique<Foo>()...後者は同じですがnew Foo()、それ以外の場合はそうです。
バリー

@barry true、オーバーロードされた演算子newが可能です。
Yakk-Adam Nevraumont 2018

@dedup C ++ 17の魔術ってなんですか?
Yakk-Adam Nevraumont 2018

2
@Deduplicatorがstd::unique_ptr許可されていない場合、c ++ 17はコンストラクターのテンプレート型の推定を導入しました。区別に関係している場合std::unique_ptr<T>std::unique_ptr<T[]>
永遠の

@ Yakk-AdamNevraumont私は新しいものをオーバーロードすることを意味したのではなく、単にdefault-initとvalue-initを意味しました。
バリー

0

それ以来、C ++ 17は評価順序を明確にし、構文Aも安全にしました

それは本当に十分ではありません。安全性の保証はそれほど強力な慣行ではないため、最近導入された技術条項に依存しています。

  • 誰かがこのコードをC ++ 14でコンパイルする可能性があります。
  • あなたは生の使用を奨励するでしょう newたとえば、例をコピーして貼り付けるなど、他の場所のます。
  • SMが示唆するように、コードの重複があるため、1つのタイプが変更されても、他のタイプは変更されない場合があります。
  • ある種の自動IDEリファクタリングは、それをnew別の場所に移動する可能性があります(承知しましたが、その可能性はほとんどありません)。

一般に、コードが適切/堅牢/明確に有効であることは、言語を敷設したり、標準の細かい技術的な句を調べたりずにことを。

(これは、タプルの破棄の順序についてここで私が本質的に主張したものと同じです。)


-1

void function(std :: unique_ptr(new A())、std :: unique_ptr(new B())){...}を検討してください

new A()は成功したが、new B()が例外をスローしたとします。これをキャッチして、プログラムの通常の実行を再開します。残念ながら、C ++標準では、オブジェクトAを破棄してメモリを解放する必要はありません。メモリが静かにリークし、クリーンアップする方法はありません。AとBをstd :: make_uniquesにラップすることで、リークが発生しないことが確実になります。

void function(std :: make_unique()、std :: make_unique()){...}ここでのポイントは、std :: make_uniqueとstd :: make_uniqueが一時オブジェクトになり、一時オブジェクトのクリーンアップが正しく指定されていることですC ++標準:デストラクタがトリガーされ、メモリが解放されます。したがって、可能であれば、常にstd :: make_uniqueおよびstd :: make_sharedを使用してオブジェクトを割り当てることをお勧めします。


4
著者は、C ++ 17でリークが発生しないことを明示的に指定しました。「それ以来、C ++ 17は評価の順序を明確にし、構文Aも安全にするため、これが私の質問です(...)」。あなたは彼の質問に答えませんでした。
R2RT
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.