Namespaces
Variants

Copy elision

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

特定の条件が満たされた場合、同じ型(CV修飾を無視)のソースオブジェクトからのクラスオブジェクトの作成は、選択されたコンストラクタやオブジェクトのデストラクタに副作用がある場合でも省略されることがあります。このオブジェクト作成の省略は copy elision と呼ばれます。

目次

説明

コピー省略は以下の状況で許可されます(複数のコピーを排除するために組み合わせることが可能):

  • クラス型の戻り値型を持つ関数内の return において、被演算子が 自動記憶域期間 を持つ非volatileオブジェクト obj (関数引数または 例外ハンドラ の引数を除く)の名前である場合、結果オブジェクトの コピー初期化 は、 obj を関数呼び出しの結果オブジェクトに直接構築することで省略できる。このコピー省略の変種は 名前付き戻り値最適化 (NRVO)として知られる。
  • クラスオブジェクト target が参照に束縛されていない一時クラスオブジェクト obj でコピー初期化される場合、コピー初期化は obj を直接 target に構築することで省略できる。このコピー省略の変種は 無名戻り値最適化 (URVO)として知られる。C++17以降、URVOは必須となり、もはやコピー省略の形式とは見なされない。下記を参照。
(C++17まで)
  • throw において、オペランドが自動記憶域期間を持つ非volatileオブジェクト obj の名前(関数パラメータまたはハンドラパラメータを除く)であり、それが最も内側の囲み try ブロック (存在する場合)を含まない スコープ に属する場合、例外オブジェクトのコピー初期化は obj を直接例外オブジェクトに構築することで省略できる。
  • ハンドラ において、ハンドラ引数のコピー初期化は、プログラムの意味がハンドラ引数のコンストラクタとデストラクタの実行を除いて変更されない場合、ハンドラパラメータを例外オブジェクトのエイリアスとして扱うことで省略できる。
(C++11以降)
  • コルーチン において、コルーチンパラメータのコピーを省略できる。この場合、プログラムの意味がパラメータコピーオブジェクトのコンストラクタとデストラクタの実行を除いて変更されない場合、そのコピーへの参照は対応するパラメータへの参照に置き換えられる。
(C++20以降)

コピー省略が発生する場合、実装は省略された初期化のソースとターゲットを、単に同じオブジェクトを参照する2つの異なる方法として扱います。

最適化が行われなかった場合の2つのオブジェクトの破棄タイミングのうち、より遅い時点で破棄が発生します。

(until C++11)

選択されたコンストラクタの最初のパラメータがオブジェクトの型への右辺値参照である場合、そのオブジェクトの破棄はターゲットが破棄される時点で発生します。それ以外の場合、破棄は最適化が行われなかった場合の2つのオブジェクトの破棄タイミングのうち、より遅い時点で発生します。

(since C++11)


Prvalueセマンティクス(「保証されたコピー省略」)

C++17以降、prvalueは必要になるまで実体化されず、最終的な格納先のストレージに直接構築されます。これは、言語構文が視覚的にコピー/ムーブを示唆している場合でも(例: copy initialization )、コピー/ムーブが実行されないことを意味します — つまり、型はアクセス可能なコピー/ムーブコンストラクタを全く持つ必要がありません。例としては:

  • return文 で返されるオブジェクトの初期化において、オペランドが関数の戻り値型と同じクラス型( cv修飾 を無視)の prvalue である場合:
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オブジェクトが破棄されない場合でも、返される型のデストラクタはreturn文の時点でアクセス可能であり、削除されていない必要があります。
  • オブジェクトの初期化において、初期化式が変数の型と同じクラス型( cv修飾 を無視)の prvalue である場合:
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 コピー(/ムーブ)操作のみが省略可能であったが、
非コピー(/非ムーブ)コンストラクタがコピー初期化で選択される可能性があった
関連するコピー初期化における
あらゆるオブジェクト構築を省略

関連項目