最も単純なケースを考えてみましょう:
lofactory( ... ).some_method();
この場合、lofactoryから caller コンテキストへの 1 つのコピーが可能ですが、RVO/NRVOによって最適化して取り除くことができます。
LargeObject mylo2 ( lofactory( ... ) );
この場合、可能なコピーは次のとおりです。
- lofactoryから呼び出し元のコンテキストに一時的に戻ります – RVO/NRVOによって最適化できます
- mylo2を一時的なものからコピー構築します – コピー省略によって最適化できます
const LargeObject& mylo1 = lofactory( ... );
この場合、まだ 1 つのコピーが可能です。
- lofactoryからcaller コンテキストに一時的に戻ります – RVO/NRVOによって最適化できます(あまりにも!)
参照はこの一時にバインドされます。
そう、
const& を一時的に使用する場合、および RVO/NRVO に依存する場合に、一般的に受け入れられているルールまたはベスト プラクティスはありますか?
上で述べたように、 の場合でもconst&
、不要なコピーが可能であり、RVO/NRVOによって最適化して削除できます。
コンパイラがRVO/NVROを適用する場合、ほとんどの場合、ステージ 2 (上記) でコピー省略が行われます。その場合、コピー省略は NRVO よりもはるかに簡単だからです。
ただし、最悪の場合、const&
ケース用に 1 つのコピーがあり、値を初期化するときに 2 つのコピーがあります。
const& メソッドを使用すると、使用しないよりも悪い状況が発生する可能性はありますか?
そのようなケースはないと思います。少なくとも、コンパイラがconst&
. (同様の状況の例として、MSVC が集合体の初期化に NVRO を実行しないことに気付きました。)
(たとえば、LargeObjectにそれらが実装されている場合、C++ 11の移動セマンティクスについて考えています...)
C++11 では、ifLargeObject
に移動セマンティクスがある場合、最悪の場合、そのconst&
ケースでは 1 つの移動があり、値を初期化すると 2 つの移動があります。だから、const&
まだ少し良いです。
したがって、コンパイラが何らかの理由でコピー省略を実行できなかった場合にコピーが妨げられる可能性があるため、可能な場合は常に一時変数を const& にバインドすることをお勧めします。
アプリケーションの実際のコンテキストを知らなくても、これは良いルールのように思えます。
C++11 では、一時的に右辺値参照 (LargeObject&&) にバインドできます。したがって、そのような一時的なものは変更できます。
ちなみに、Move セマンティック エミュレーションは、さまざまなトリックによって C++98/03 で利用できます。例えば:
ただし、移動セマンティックが存在する場合でも、安価に移動できないオブジェクトがあります。たとえば、内部に double data[4][4] を持つ 4x4 行列クラス。そのため、コピー省略 RVO/NRVO は、C++11 でも依然として非常に重要です。ちなみに、Copy-elision/RVO/NRVO が発生すると、Move よりも高速です。
PS、実際のケースでは、考慮すべき追加事項がいくつかあります。
たとえば、ベクトルを返す関数がある場合、Move/RVO/NRVO/Copy-Elision が適用されても、100% 効率的ではない可能性があります。たとえば、次のケースを考えてみましょう。
while(/*...*/)
{
vector<some> v = produce_next(/* ... */); // Move/RVO/NRVO are applied
// ...
}
コードを次のように変更すると、より効率的になります。
vector<some> v;
while(/*...*/)
{
v.clear();
produce_next( v ); // fill v
// or something like:
produce_next( back_inserter(v) );
// ...
}
この場合、 v.capacity() が十分な場合、ベクター内で既に割り当てられているメモリを再利用できるため、反復ごとに Produce_next 内で新しい割り当てを行う必要はありません。