Copy elision
特定の条件が満たされた場合、同じ型(CV修飾を無視)のソースオブジェクトからのクラスオブジェクトの作成は、選択されたコンストラクタやオブジェクトのデストラクタに副作用がある場合でも省略されることがあります。このオブジェクト作成の省略は copy elision と呼ばれます。
目次 |
説明
コピー省略は以下の状況で許可されます(複数のコピーを排除するために組み合わせることが可能):
- クラス型の戻り値型を持つ関数内の return 文 において、被演算子が 自動記憶域期間 を持つ非volatileオブジェクト obj (関数引数または 例外ハンドラ の引数を除く)の名前である場合、結果オブジェクトの コピー初期化 は、 obj を関数呼び出しの結果オブジェクトに直接構築することで省略できる。このコピー省略の変種は 名前付き戻り値最適化 (NRVO)として知られる。
|
(C++17まで) |
| (C++11以降) | |
|
(C++20以降) |
コピー省略が発生する場合、実装は省略された初期化のソースとターゲットを、単に同じオブジェクトを参照する2つの異なる方法として扱います。
|
最適化が行われなかった場合の2つのオブジェクトの破棄タイミングのうち、より遅い時点で破棄が発生します。 |
(until C++11) |
|
選択されたコンストラクタの最初のパラメータがオブジェクトの型への右辺値参照である場合、そのオブジェクトの破棄はターゲットが破棄される時点で発生します。それ以外の場合、破棄は最適化が行われなかった場合の2つのオブジェクトの破棄タイミングのうち、より遅い時点で発生します。 |
(since C++11) |
Prvalueセマンティクス(「保証されたコピー省略」)C++17以降、prvalueは必要になるまで実体化されず、最終的な格納先のストレージに直接構築されます。これは、言語構文が視覚的にコピー/ムーブを示唆している場合でも(例: copy initialization )、コピー/ムーブが実行されないことを意味します — つまり、型はアクセス可能なコピー/ムーブコンストラクタを全く持つ必要がありません。例としては: T f() { return U(); // constructs a temporary of type U, // then initializes the returned T from the temporary { T g() { return T(); // constructs the returned T directly; no move {
T x = T(T(f())); // x is initialized by the result of f() directly; no move
struct C { /* ... */ }; C f(); struct D; D g(); struct D : C { D() : C(f()) {} // no elision when initializing a base class subobject D(int) : D(g()) {} // no elision because the D object being initialized might // be a base-class subobject of some other class }; 注記:このルールは最適化を指定しておらず、標準はこれを正式に「コピー省略」とは記述していません(省略されるものがないため)。代わりに、C++17のコア言語仕様における prvalues と 一時オブジェクト の定義は、以前のC++リビジョンとは根本的に異なります:コピー/ムーブ元の一時オブジェクトが存在しなくなります。C++17のメカニズムを説明する別の方法は、「非実体化値渡し」または「遅延一時オブジェクト実体化」です:prvaluesは一時オブジェクトを実体化することなく返され、使用されます。 |
(C++17以降) |
注記
コピー省略は 唯一許可された最適化形式 (C++14まで) 許可された2つの最適化形式の1つであり、 アロケーション省略と拡張 と並んで (C++14以降) 観測可能な副作用を変更できる。一部のコンパイラは許可されているすべての状況(例えば、デバッグモード)でコピー省略を実行しないため、コピー/ムーブコンストラクタとデストラクタの副作用に依存するプログラムは移植性がない。
注意点: - HTMLタグと属性はそのまま保持 - C++固有の用語(copy elision, allocation elision and extension, copy/move constructors, destructorsなど)は翻訳せず - 技術的な正確性と専門性を維持 - 括弧内のバージョン表記はC++の慣例に従って翻訳|
return 文または throw 式において、コンパイラがコピー省略を実行できないがコピー省略の条件が満たされている場合、またはソースが関数パラメータであることを除けば条件が満たされる場合、 ソースオペランドが左値で指定されている場合でもコンパイラはムーブコンストラクタの使用を試みる (C++23まで) ソースオペランドは右値として扱われる (C++23以降) 。詳細は return statement を参照。 定数式 および 定数初期化 では、コピー省略は決して行われない。 struct A { void* p; constexpr A() : p(this) {} A(const A&); // Disable trivial copyability }; constexpr A a; // OK: a.p points to a constexpr A f() { A x; return x; } constexpr A b = f(); // error: b.p would be dangling and point to the x inside f constexpr A c = A(); // (until C++17) error: c.p would be dangling and point to a temporary // (since C++17) OK: c.p points to c; no temporary is involved |
(C++11以降) |
| 機能テストマクロ | 値 | 標準 | 機能 |
|---|---|---|---|
__cpp_guaranteed_copy_elision
|
201606L
|
(C++17) | 簡略化された 値カテゴリ による保証されたコピー省略 |
例
#include <iostream> struct Noisy { Noisy() { std::cout << "constructed at " << this << '\n'; } Noisy(const Noisy&) { std::cout << "copy-constructed\n"; } Noisy(Noisy&&) { std::cout << "move-constructed\n"; } ~Noisy() { std::cout << "destructed at " << this << '\n'; } }; Noisy f() { Noisy v = Noisy(); // (until C++17) copy elision initializing v from a temporary; // the move constructor may be called // (since C++17) "guaranteed copy elision" return v; // copy elision ("NRVO") from v to the result object; // the move constructor may be called } void g(Noisy arg) { std::cout << "&arg = " << &arg << '\n'; } int main() { Noisy v = f(); // (until C++17) copy elision initializing v from the result of f() // (since C++17) "guaranteed copy elision" std::cout << "&v = " << &v << '\n'; g(f()); // (until C++17) copy elision initializing arg from the result of f() // (since C++17) "guaranteed copy elision" }
出力例:
constructed at 0x7fffd635fd4e &v = 0x7fffd635fd4e constructed at 0x7fffd635fd4f &arg = 0x7fffd635fd4f destructed at 0x7fffd635fd4f destructed at 0x7fffd635fd4e
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用バージョン | 公開時の仕様 | 修正後の仕様 |
|---|---|---|---|
| CWG 1967 | C++11 |
ムーブコンストラクタを使用したコピー省略が行われる場合、
ムーブ元オブジェクトの生存期間が依然考慮されていた |
考慮されない |
| CWG 2426 | C++17 | 純粋右値を返す際にデストラクタが要求されていなかった | デストラクタが潜在的に呼び出される |
| CWG 2930 | C++98 |
コピー(/ムーブ)操作のみが省略可能であったが、
非コピー(/非ムーブ)コンストラクタがコピー初期化で選択される可能性があった |
関連するコピー初期化における
あらゆるオブジェクト構築を省略 |