2016年のOuluISO C ++標準会議で、標準化委員会によって、簡略化された値のカテゴリによる保証されたコピーの省略と呼ばれる提案がC ++ 17に投票されました。
保証されたコピーの省略はどの程度正確に機能しますか?コピーの省略がすでに許可されているいくつかのケースをカバーしていますか、それともコピーの省略を保証するためにコードの変更が必要ですか?
2016年のOuluISO C ++標準会議で、標準化委員会によって、簡略化された値のカテゴリによる保証されたコピーの省略と呼ばれる提案がC ++ 17に投票されました。
保証されたコピーの省略はどの程度正確に機能しますか?コピーの省略がすでに許可されているいくつかのケースをカバーしていますか、それともコピーの省略を保証するためにコードの変更が必要ですか?
回答:
コピーの省略は、さまざまな状況で発生することが許可されていました。ただし、許可されていても、コピーが省略されていないかのようにコードが機能する必要がありました。つまり、アクセス可能なコピーおよび/または移動コンストラクターが必要でした。
保証されたコピーの省略は、コピー/移動が省略される可能性がある特定の状況が実際にコピー/移動をまったく引き起こさないように、多くの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)については何も変更されません。
std::function<T()>
本当に(ワンショット)だと思います。