web-dev-qa-db-ja.com

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

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

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

(説明:評価が最初に生のポインター用のメモリーを割り振ってからg()を呼び出し、std::unique_ptr構造の前に例外がスローされると、メモリーはリークされます。)

std::make_uniqueを呼び出すことは呼び出しの順番を制限するための方法であり、したがって物事を安全にします。

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

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

今のところ、私が想像できる唯一の理由はMyClassを一度だけタイプすることができるということです(std::unique_ptr<Base>(new Derived(param))の多態性に頼る必要がないと仮定して)。ただし、特にstd::make_uniqueのコンストラクターでは許可されているのに対し、std::unique_ptrでは削除文字の指定が許可されていない場合は、これはかなり弱い理由のように思われます。

念のために言っておきますが、私は標準ライブラリからstd::make_uniqueを削除することを支持してはいません(少なくとも下位互換性のためには意味があります)が、それでもstd::unique_ptrを優先する状況がまだあるのではないかと思います。

81
Eternal

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

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

63
NathanOliver

make_uniqueTT[]およびT[N]と区別しますが、unique_ptr(new ...)は区別しません。

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

42
Caleth

その理由は、重複することなくコードを短くすることです。比較する

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

MyClassnew、中括弧を保存します。 ptr と比較すると、 make の方が1文字だけ多くなります。

20
S.M.

newを使用するたびに、生涯の正確性について特別に注意深く監査する必要があります。それは削除されますか? 1回だけ?

make_uniqueを使用するたびにこれらの追加の特性が得られるわけではありません。所有オブジェクトが「正しい」存続期間を持つ限り、それは再帰的にユニークポインタを「正しい」にします。

さて、unique_ptr<Foo>(new Foo())はすべての点で同一です1 make_unique<Foo>()へ。監査するためにnewを使用するには、より単純な「ソースコードをgrepする」だけが必要です。


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

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

それは本当に十分ではありません。安全性の保証として最近導入された技術条項に依存することは、非常に堅牢なプラクティスではありません。

  • 誰かがこのコードをC++ 14でコンパイルする可能性があります。
  • 生のnewを他の場所で使用することを奨励します。例をコピーして貼り付けます。
  • S.M.としてコードの重複があるため、一方のタイプを変更せずに他方のタイプを変更することをお勧めします。
  • ある種の自動IDEリファクタリングは、そのnewを他の場所に移動する可能性があります(OK、認められました、その可能性はあまりありません)。

一般的に、コードが適切/堅牢/明確に有効であることは良い考えですwithout標準。

(これは基本的に私が行ったのと同じ引数です ここ タプル破壊の順序について。)

0
einpoklum