ここでは、RVO をいつ実行できるかについて多くの議論が行われていますが、実際にいつ実行されるかについてはあまり議論されていません。何度も述べているように、標準に従って RVO を保証することはできませんが、RVO の最適化が成功するか、対応するコードがコンパイルに失敗することを保証する方法はありますか?
これまでのところ、RVO が失敗したときにコードがリンク エラーを発行するようにすることに部分的に成功しました。このため、コピー コンストラクターを定義せずに宣言します。x(x&&)
明らかに、これは、1 つまたは両方のコピー コンストラクター、つまりとを実装する必要があるまれではないケースでは、堅牢でも実行可能でもありませんx(x const&)
。
これは私の 2 番目の質問につながります。コンパイラの作成者は、ユーザー定義のコピー コンストラクターが存在する場合に RVO を有効にすることを選択し、既定のコピー コンストラクターのみが存在する場合には有効にしないのはなぜですか?
3 番目の質問:プレーン データ構造に対して RVO を有効にする他の方法はありますか?
最後の質問 (約束):私のテスト コードを、gcc と clang で観察した以外の動作にするコンパイラを知っていますか?
問題を示す gcc 4.6、gcc 4.8、clang 3.3 のサンプル コードを次に示します。この動作は、一般的な最適化またはデバッグ設定には依存しません。もちろん、オプション--no-elide-constructors
はそれが言うことを行います。つまり、RVO をオフにします。
#include <iostream>
using namespace std;
struct x
{
x () { cout << "original x address" << this << endl; }
};
x make_x ()
{
return x();
}
struct y
{
y () { cout << "original y address" << this << endl; }
// Any of the next two constructors will enable RVO even if only
// declared but not defined. Default constructors will not do!
y(y const & rhs);
y(y && rhs);
};
y make_y ()
{
return y();
}
int main ()
{
auto x1 = make_x();
cout << "copy of x address" << &x1 << endl;
auto y1 = make_y();
cout << "copy of y address" << &y1 << endl;
}
出力:
original x address0x7fff8ef01dff
copy of x address0x7fff8ef01e2e
original y address0x7fff8ef01e2f
copy of y address0x7fff8ef01e2f
RVO はプレーンなデータ構造でも動作しないようです:
#include <iostream>
using namespace std;
struct x
{
int a;
};
x make_x ()
{
x tmp;
cout << "original x address" << &tmp << endl;
return tmp;
}
int main ()
{
auto x1 = make_x();
cout << "copy of x address" << &x1 << endl;
}
出力:
original x address0x7fffe7bb2320
copy of x address0x7fffe7bb2350
更新:一部の最適化は RVO と非常に簡単に混同されることに注意してください。のようなコンストラクタ ヘルパーmake_x
がその例です。最適化が実際に標準によって強制されているこの例を参照してください。