Move constructors
ムーブコンストラクタは、同じクラス型の引数で呼び出せ、引数の内容をコピーし(場合によっては引数を変更する可能性がある) コンストラクタ です。
目次 |
構文
class-name
(
parameter-list
);
|
(1) | ||||||||
class-name
(
parameter-list
)
function-body
|
(2) | ||||||||
class-name
(
single-parameter-list
) = default;
|
(3) | ||||||||
class-name
(
parameter-list
) = delete;
|
(4) | ||||||||
class-name
::
class-name
(
parameter-list
)
function-body
|
(5) | ||||||||
class-name
::
class-name
(
single-parameter-list
) = default;
|
(6) | ||||||||
タグ内のテキスト、C++固有の用語は翻訳せず、元のフォーマットを保持しています。テキスト部分は日本語に翻訳する必要がありませんでした。
| class-name | - | ムーブコンストラクタが宣言されているクラス |
| parameter-list | - |
以下のすべての条件を満たす空でない
パラメータリスト
:
|
| single-parameter-list | - | 唯一のパラメータが T && 、 const T && 、 volatile T && または const volatile T && 型であり、デフォルト引数を持たない パラメータリスト |
| function-body | - | ムーブコンストラクタの 関数本体 |
説明
struct X { X(X&& other); // ムーブコンストラクタ // X(X other); // エラー: 不正なパラメータ型 }; union Y { Y(Y&& other, int num = 1); // 複数パラメータを持つムーブコンストラクタ // Y(Y&& other, int num); // エラー: `num`にデフォルト引数がありません };
ムーブコンストラクタは通常、オブジェクトが同じ型の 右辺値 (xvalue または prvalue) (C++17まで) xvalue (C++17以降) から 初期化 される際( 直接初期化 または コピー初期化 によって)呼び出されます。これには以下が含まれます:
-
初期化:
T a
=
std
::
move
(
b
)
;
または
T a
(
std
::
move
(
b
)
)
;
、ここで
b
は型
Tを持つ; -
関数引数の受け渡し:
f
(
std
::
move
(
a
)
)
;
、ここで
a
は型
Tを持ち、 f は void f ( T t ) である; -
関数の戻り値:
return
a
;
、
T f
(
)
のような関数内で使用され、ここで
a
は型
Tを持ち、ムーブコンストラクタを持つ;
初期化子がprvalueの場合、ムーブコンストラクタの呼び出しは 多くの場合最適化されて除去される (C++17まで) 行われない (C++17以降) 。詳細は copy elision を参照。
ムーブコンストラクタは通常、引数が保持するリソース(動的に確保されたオブジェクトへのポインタ、ファイル記述子、TCPソケット、スレッドハンドルなど)をコピーするのではなく転送し、引数を有効だが不定な状態にします。ムーブコンストラクタは引数の寿命を変更しないため、後で引数のデストラクタが呼び出されることになります。例えば、 std::string や std::vector からのムーブでは、引数が空の状態になる可能性があります。 std::unique_ptr などの一部の型では、ムーブ後の状態が完全に規定されています。
暗黙的に宣言されるムーブコンストラクタ
クラス型に対してユーザー定義のムーブコンストラクタが提供されておらず、以下の条件がすべて満たされる場合:
- ユーザー宣言された copy constructor が存在しない;
- ユーザー宣言された copy assignment operator が存在しない;
- ユーザー宣言された move assignment operator が存在しない;
- ユーザー宣言された destructor が存在しない。
その後、コンパイラは以下のシグネチャを持つ非 explicit inline public メンバーとして、そのクラスのmoveコンストラクタを宣言します: T :: T ( T && ) 。
クラスは複数のムーブコンストラクタを持つことができます。例えば、 T :: T ( const T && ) と T :: T ( T && ) の両方です。ユーザー定義のムーブコンストラクタが存在する場合でも、ユーザーは default キーワードを使用して、暗黙的に宣言されたムーブコンストラクタの生成を強制することができます。
暗黙的に宣言された(または最初の宣言でdefault指定された)ムーブコンストラクタは、以下のように例外仕様を持ちます: 動的例外仕様 (C++17まで) noexcept仕様 (C++17以降) 。
暗黙的に定義されるムーブコンストラクタ
暗黙的に宣言されたムーブコンストラクタが削除もトリビアルもされない場合、それが ODR-use または 定数評価に必要 とされる場合、コンパイラによって定義されます(つまり、関数本体が生成されコンパイルされます)。共用体型の場合、暗黙的に定義されたムーブコンストラクタはオブジェクト表現をコピーします( std::memmove によるように)。非共用体クラス型の場合、ムーブコンストラクタはオブジェクトの直接基底サブオブジェクトとメンバーサブオブジェクトに対して、それらの初期化順序で、 xvalue 引数を用いた直接初期化による完全なメンバーごとのムーブを実行します。参照型の非静的データメンバーごとに、ムーブコンストラクタはその参照を、ソース参照がバインドされているのと同じオブジェクトまたは関数にバインドします。
これが
constexpr
コンストラクタ
(C++23まで)
constexpr
関数
(C++23以降)
の要件を満たす場合、生成されたムーブコンストラクタは
constexpr
となります。
削除されたムーブコンストラクタ
クラス
T
の暗黙的に宣言された、または明示的にデフォルト化されたムーブコンストラクタは、以下の条件を満たすクラス型
M
(またはその多次元配列)の
潜在的に構築されるサブオブジェクト
を
T
が持つ場合、削除済みとして定義されます。
-
Mが削除されたデストラクタを持つか、コピーコンストラクタからアクセスできない場合、または -
Mのムーブコンストラクタを検索するために適用されるオーバーロード解決
-
- 使用可能な候補が得られない場合、または
- サブオブジェクトが variant member である場合に、非自明な関数を選択する場合。
このようなコンストラクタは オーバーロード解決 によって無視されます(そうでなければ、右辺値からのコピー初期化を妨げることになります)。
自明なムーブコンストラクタ
T
クラスのムーブコンストラクタは、以下のすべての条件を満たす場合にトリビアルとなります:
- ユーザー提供ではない(つまり、暗黙的に定義されるかデフォルト化されている);
-
Tに仮想メンバ関数がない; -
Tに仮想基底クラスがない; -
Tのすべての直接基底クラスに対して選択されたムーブコンストラクタがトリビアルである; -
Tのすべての非静的クラス型(またはクラス型の配列)メンバに対して選択されたムーブコンストラクタがトリビアルである。
自明なムーブコンストラクタは、自明なコピーコンストラクタと同じ動作を行うコンストラクタであり、つまり std::memmove によってオブジェクト表現のコピーを作成します。C言語と互換性のあるすべてのデータ型は自明にムーブ可能です。
適格なムーブコンストラクタ
|
ムーブコンストラクタは、削除されていない場合に適格となる。 |
(C++20まで) |
|
ムーブコンストラクタは、以下の条件がすべて満たされる場合に適格となる: |
(C++20以降) |
有効なムーブコンストラクタの自明性は、クラスが 暗黙的寿命型 であるかどうか、およびクラスが 自明コピー可能型 であるかどうかを決定します。
注記
strong exception guarantee を実現可能にするため、ユーザー定義のmoveコンストラクタは例外を投げるべきではありません。例えば、 std::vector は要素の再配置が必要な場合、 std::move_if_noexcept を使用してmoveとcopyの選択を行います。
コピーコンストラクタとムーブコンストラクタの両方が提供され、他に有効なコンストラクタがない場合、引数が同じ型の
右辺値
(
将亡値
(
std::move
の結果など)または
純粋右辺値
(名前のない一時オブジェクトなど)
(C++17まで)
)の場合、オーバーロード解決はムーブコンストラクタを選択し、引数が
左辺値
(名前付きオブジェクトまたは左辺値参照を返す関数/演算子)の場合、コピーコンストラクタを選択します。コピーコンストラクタのみが提供されている場合、すべての引数カテゴリがそれを選択します(右辺値がconst参照にバインドできるため、constへの参照を取る限り)。これにより、ムーブが利用できない場合、コピーがムーブの代替手段となります。
例
#include <iomanip> #include <iostream> #include <string> #include <utility> struct A { std::string s; int k; A() : s("test"), k(-1) {} A(const A& o) : s(o.s), k(o.k) { std::cout << "move failed!\n"; } A(A&& o) noexcept : s(std::move(o.s)), // クラス型メンバーの明示的なムーブ k(std::exchange(o.k, 0)) // 非クラス型メンバーの明示的なムーブ {} }; A f(A a) { return a; } struct B : A { std::string s2; int n; // 暗黙的なムーブコンストラクタ B::(B&&) // Aのムーブコンストラクタを呼び出す // s2のムーブコンストラクタを呼び出す // nのビット単位コピーを行う }; struct C : B { ~C() {} // デストラクタが暗黙的なムーブコンストラクタ C::(C&&) を妨げる }; struct D : B { D() {} ~D() {} // デストラクタは暗黙的なムーブコンストラクタ D::(D&&) を妨げる D(D&&) = default; // それでもムーブコンストラクタを強制する }; int main() { std::cout << "Trying to move A\n"; A a1 = f(A()); // 戻り値によるムーブ構築が関数パラメータからターゲットへ行われる // std::cout << "Before move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; A a2 = std::move(a1); // xvalueからのムーブ構築 std::cout << "After move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; std::cout << "\nTrying to move B\n"; B b1; std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "\n"; B b2 = std::move(b1); // 暗黙的なムーブコンストラクタを呼び出す std::cout << "After move, b1.s = " << std::quoted(b1.s) << "\n"; std::cout << "\nTrying to move C\n"; C c1; C c2 = std::move(c1); // コピーコンストラクタを呼び出す std::cout << "\nTrying to move D\n"; D d1; D d2 = std::move(d1); }
出力:
Trying to move A Before move, a1.s = "test" a1.k = -1 After move, a1.s = "" a1.k = 0 Trying to move B Before move, b1.s = "test" After move, b1.s = "" Trying to move C move failed! Trying to move D
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 1353 | C++11 |
デフォルト化されたムーブコンストラクタが削除定義される条件が
多次元配列型を考慮していなかった |
これらの型を考慮する |
| CWG 1402 | C++11 |
非自明なコピーコンストラクタを呼び出すデフォルト化された
ムーブコンストラクタは削除定義されていた;削除定義された デフォルト化ムーブコンストラクタはオーバーロード解決に参加していた |
そのようなコピーコンストラクタの呼び出しを許可;
オーバーロード解決では無視されるように変更 |
| CWG 1491 | C++11 |
右辺値参照型の非静的データメンバを持つクラスの
デフォルト化ムーブコンストラクタが削除定義されていた |
この場合では削除定義されない |
| CWG 2094 | C++11 |
volatile サブオブジェクトがデフォルト化された
ムーブコンストラクタを非自明にしていた( CWG issue 496 ) |
自明性に影響しない |
| CWG 2595 | C++20 |
より制約されているが関連制約を満たさない
別のムーブコンストラクタが存在する場合、 ムーブコンストラクタが適格ではなかった |
この場合でも適格となりうる |