保証されたコピーの省略はどのように機能しますか?


89

2016年のOuluISO C ++標準会議で、標準化委員会によって、簡略化された値のカテゴリによる保証されたコピーの省略と呼ばれる提案がC ++ 17に投票されました。

保証されたコピーの省略はどの程度正確に機能しますか?コピーの省略がすでに許可されているいくつかのケースをカバーしていますか、それともコピーの省略を保証するためにコードの変更が必要ですか?

回答:


129

コピーの省略は、さまざまな状況で発生することが許可されていました。ただし、許可されていても、コピーが省略されていないかのようにコードが機能する必要がありました。つまり、アクセス可能なコピーおよび/または移動コンストラクターが必要でした。

保証されたコピーの省略は、コピー/移動が省略される可能性がある特定の状況が実際にコピー/移動をまったく引き起こさないように、多くのC ++の概念を再定義します。コンパイラはコピーを削除していません。標準では、そのようなコピーは発生しないとされています。

この関数について考えてみましょう。

T Func() {return T();}

保証されていないコピーの省略ルールでは、これにより一時が作成され、その一時から関数の戻り値に移動します。その移動操作省略できますが、Tできますが、使用されない場合でも、アクセス可能な移動コンストラクター必要です。

同様に:

T t = Func();

これはのコピー初期化ですt。これtにより、戻り値がFunc。のコピー初期化が行われます。ただし、T呼び出されない場合でも、moveコンストラクターが必要です。

保証されたコピーの省略は、prvalue式の意味を再定義します。C ++ 17より前では、prvaluesは一時的なオブジェクトです。17 C ++では、prvalue表現が可能なものだけである実体一時的には、それはまだ一時的ではありません。

prvalueを使用してprvalueのタイプのオブジェクトを初期化する場合、一時的なものは実体化されません。を実行するreturn T();と、prvalueを介して関数の戻り値が初期化されます。その関数はを返すTため、一時的なものは作成されません。prvalueの初期化は、単に戻り値を直接初期化します。

理解しておくべきことは、戻り値はprvalueであるため、まだオブジェクトではないということです。これは、オブジェクトの初期化子にすぎませんT()

を実行T t = Func();すると、戻り値のprvalueがオブジェクトを直接初期化しますt。「一時的なコピー/移動の作成」段階はありません。以来Func()の戻り値がprvalue相当しT()t直接によって初期化されT()、あなたが行っていたかのように正確に、T t = T()

prvalueが他の方法で使用される場合、prvalueは一時オブジェクトを実体化し、そのオブジェクトはその式で使用されます(または、式がない場合は破棄されます)。したがってconst T &rt = Func();、そうすると、prvalueは(T()初期化子として使用して)一時的なものを実体化し、その参照はrt、通常の一時的な存続期間延長のものとともに、に格納されます。

エリジオンが保証されていることの1つは、動かないオブジェクトを返すことです。たとえば、lock_guardコピーや移動はできないため、値で返す関数を使用することはできません。しかし、コピーの省略が保証されていれば、それは可能です。

保証された省略は、直接初期化でも機能します。

new T(FactoryFunction());

場合はFactoryFunction戻りT値で、この式は、割り当てられたメモリへの戻り値はコピーされません。代わりに、メモリを割り当て、割り当てられたメモリを関数呼び出しの戻り値メモリとして直接使用します。

したがって、値で返されるファクトリ関数は、ヒープに割り当てられたメモリを知らなくても直接初期化できます。もちろん、これらの機能が内部的に保証されたコピーの省略のルールに従っている限り。タイプのprvalueを返す必要がありTます。

もちろん、これも機能します。

new auto(FactoryFunction());

タイプ名を書くのが嫌いな場合に備えて。


上記の保証はprvaluesに対してのみ機能することを認識することが重要です。つまり、名前付き変数を返す場合、保証はありません。

T Func()
{
   T t = ...;
   ...
   return t;
}

この場合tでも、アクセス可能なコピー/移動コンストラクターが必要です。はい、コンパイラはコピー/移動を最適化することを選択できます。ただし、コンパイラーは、アクセス可能なコピー/移動コンストラクターの存在を確認する必要があります。

したがって、名前付き戻り値の最適化(NRVO)については何も変更されません。


1
@BenVoigt:自明にコピーできないユーザー定義型をレジスターに入れることは、エリジオンが利用可能かどうかにかかわらず、ABIが実行できる実行可能なことではありません。
ニコルボーラス2016年

1
ルールが公開されたので、これを「prvaluesareinitializations」の概念で更新する価値があるかもしれません。
Johannes Schaub-litb 2016

6
@ JohannesSchaub-litb:C ++標準の特徴について完全に理解しすぎている場合にのみ、「あいまい」になります。C ++コミュニティの99%にとって、「保証されたコピーの省略」が何を指しているのかを知っています。この機能を提案している実際の論文は、「保証されたコピーの省略」とさえ題されています。「簡略化された値のカテゴリを介して」を追加すると、ユーザーが混乱し、理解しにくくなるだけです。また、これらのルールは値のカテゴリに関するルールを実際には「単純化」していないため、これは誤った呼び方です。好むと好まざるとにかかわらず、「保証されたコピーの省略」という用語は、この機能だけを指します。
ニコルボーラス2017年

1
私は、prvalueを拾い上げて持ち運びできるようにしたいと思っています。これはstd::function<T()>本当に(ワンショット)だと思います。
Yakk-Adam Nevraumont 2017年

1
@LukasSalich:それはC ++ 11の質問です。この答えはC ++ 17の機能についてです。
ニコルボーラス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.