関数の戻り値のコピーを発生させない手法として、RVO (Return Value Optimization) やNRVO (Named Return Value Optimization) といった最適化がありました。
// RVOの最適化が動作した場合 struct Foo {}; Foo foo() { return Foo(); } Foo x = foo(); // Foo型のコピーコンストラクタが動作することなくxが初期化される
// NRVOの最適化が動作した場合 struct Foo { int value = 0; }; Foo foo() { Foo y; y.value = 42; return y; } Foo x = foo(); // Foo型のコピーコンストラクタが動作することなくxが初期化される
しかし、これらの最適化はコンパイラに対して許可された動作であって、そのように最適化されることが保証されるものではありません。そのため、実際には(N)RVOによってコピーは起こらないけどコピーコンストラクタは用意しなければならない、といったことになります。
C++1zでは、このようなコピー省略を保証する仕組みが導入されます。そのため、オブジェクトの初期化のために使用するのであれば、コピーもムーブもできない型であっても、関数の戻り値として返せるようになります。
// C++1z struct Foo { // Fooはコピーもムーブもできない Foo() = default; Foo(const Foo&) = delete; Foo(Foo&&) = delete; }; Foo foo() { return Foo(); } Foo y = foo(); // OK
このコピー省略のためには、C++11で右辺値参照を導入するときに規定された「値カテゴリー (value category)」の仕様を利用します。prvalueという一時オブジェクトを表すカテゴリーの値を、オブジェクトの初期化のために使用する場合に、コピーが省略されるという仕様になります。
参照
- P0135R1 Wording for guaranteed copy elision through simplified value categories
- P0135R0 Guaranteed copy elision through simplified value categories
- Guaranteed Copy Elision
お断り
この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。
更新履歴
- 2017/01/24 22:18 : Twitterで @530506 さんから指摘を受け、C++1zの例で
Foo
型に不足していたデフォルトコンストラクタを追加