NRVO(copy elision)と関数パラメータ変数

C++11標準規格で許容される最適化 "copy elision"*1 と、関数パラメータ(仮引数)変数との関係についてメモ。

  • return文に関数パラメータ変数を指定しても、コンパイラにて "copy elision" が行われることは無い*2。つまり戻り値オブジェクト生成のために、必ずコンストラクタが呼び出される。
  • return文に関数パラメータ変数を指定した場合、ムーブコンストラクトによる戻り値オブジェクト生成を試みる*3。(暗黙のムーブ
  • おまけ:return文に通常のローカル変数を指定した場合は、コンパイラにて "copy elision" が行われる可能性がある。
// コピー&ムーブ可能なクラス型
class X { /*...*/ };

X f0()
  X x;
  return x;  // "copy elision"またはムーブコンストラクタ呼出

X f1(X x)
  return x;  // ムーブコンストラクタ呼出(copy elision無し)

N3337 12.8/p31-32より一部引用(下線部は強調)。

31 When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. (snip) This elision of copy/move operations, called copy elision, is permitted in the following circumstances (snip):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
  • (snip)

32 When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. (snip)


*1:return文における "名前付き戻り値最適化(NRVO; named return value optimization)" または "戻り値最適化(RVO; return value optimization)" という名称の方がたぶん有名。

*2:"関数のパラメータ変数" と "catch節のパラメータ変数" は明示的に除外されている。(N3337 12.8/p31)

*3:正確には “関数パラメータ変数であること以外はcopy elisionの条件を満足する” 場合において、“関数パラメータ変数(=lvalue)を指定しても、まずはrvalueとみなしてコンストラクタのオーバーロード解決を試みる” という動作。前者でエラーとなる場合は、改めてlvalueとみなしてコンストラクタ選択が行われる。(N3337 12.8/p32)