クラス T
の移動コンストラクタとは、最初のパラメータが T&&, const T&&, volatile T&&, const volatile T& で、その他のパラメータはないか、残りのパラメータはすべてデフォルト値を持つ、非テンプレート型のコンストラクタのことである。
Contents
- 1 構文
- 2 説明
- 3 暗黙的に宣言された移動コンストラクタ
- 4 暗黙的に宣言された移動コンストラクタを削除
- 5 Trivial move constructor
- 6 Eligible move constructor
- 7 暗黙的な-
- 8>
- 9定義済み移動コンストラクタ
- 8 注意事項
- 9 例
- 10 欠陥報告
- 11 参照
構文
class_name ( class_name && ) |
(1) | (C++11以降) | |||||||
class_name ( class_name && ) = default; |
(2) | (C++11 以降) | |||||||
class_name ( class_name && )=delete.とする。 |
(3) | (C++11 以降) | |||||||
ここで class_name は現在のクラス(またはクラス テンプレート内の現在のインスタンス)を指定しなければならず、ネームスペース スコープまたは友人宣言で宣言される場合は修飾クラス名でなければなりません。
解説
移動コンストラクタは通常、
- 初期化を含め、同じ型の rvalue(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); - function return: return a; T f() のような関数内部で
a
はT
型で move コンストラクタを持っているものです。
初期化子が prvalue の場合、移動コンストラクタ呼び出しは (C++17 まで) 最適化されずに (C++17 から) 行われないことが多く、コピー エリシオンを参照。たとえば、動的に割り当てられたオブジェクトへのポインタ、ファイル記述子、TCP ソケット、I/O ストリーム、実行中のスレッドなど)、それらのコピーを作成するのではなく、引数によって保持されるリソースを「盗む」、引数を有効だがそれ以外は不確定な状態のままにしておきます。 例えば、std::stringやstd::vectorから移動した場合、引数は空のまま残されることがあります。 しかし、この動作に依存してはいけません。 std::unique_ptrのようないくつかの型では、移動元の状態が完全に指定されています。
暗黙的に宣言された移動コンストラクタ
クラス型(構造体、クラス、ユニオン)に対してユーザー定義の移動コンストラクタが提供されておらず、以下のすべてが真である場合。
- ユーザーが宣言したコピーコンストラクタがない;
- ユーザーが宣言したコピー代入演算子がない;
- ユーザーが宣言した移動代入演算子がない;
- ユーザーが宣言したデストラクタもない。
では、コンパイラは移動コンストラクタをクラスの非明示的な inline public
メンバとして T::T(T&&)
のシグネチャで宣言することになります。
クラスは複数の移動コンストラクタを持つことができ、例えば T::T(const T&&) と T::T(T&&) の両方が挙げられます。 ユーザ定義の移動コンストラクタがいくつか存在する場合でも、ユーザはキーワード default
を使用して暗黙的に宣言された移動コンストラクタを強制的に生成することができます。
暗黙的に宣言された(または最初の宣言で既定された)移動コンストラクタには、動的例外仕様(C++17 まで)例外仕様(C++17 から)
削除された暗黙的に宣言された移動コンストラクタ
クラス T
に対して暗黙的に宣言されたまたは既定の移動コンストラクタは、以下のいずれかが真の場合に削除と見なされるように定義されます。
-
T
には移動できない非静的データ メンバがある (削除された、アクセスできない、またはあいまいな移動コンストラクタがある)、 -
T
には移動できない直接または仮想ベース クラスがある (削除された、アクセスできない、またはあいまいな移動コンストラクタがある)、など。 -
T
が削除された、またはアクセスできないデストラクタを持つ直接または仮想基底クラスを持っている。 -
T
はユニオンライククラスで自明ではない移動コンストラクタを持つバリアントメンバーを持っている。
削除されたデフォルトの移動コンストラクタは、オーバーロード解決によって無視されます (そうでなければ、rvalue からのコピー初期化を阻止します)。
Trivial move constructor
クラス T
の move constructor は、以下のすべてが真であれば trivial であると言えます。
- ユーザーが提供するものではない(暗黙的に定義されているかデフォルトであることを意味する)、
-
T
に仮想メンバー関数がない、 -
T
に仮想ベースクラスがない。 -
T
のすべての直接のベースに対して選択される移動コンストラクタはつまらないものである; -
T
のすべての非静的クラス型 (またはクラス型の配列) メンバに対して選択される移動コンストラクタはつまらないものである。
つまらない移動コンストラクタとは、つまらないコピーコンストラクタと同じ動作、つまり std::memmove によるかのようにオブジェクト表現のコピーを作成するコンストラクタを指します。 C言語と互換性のあるすべてのデータ型(POD型)は些細な移動が可能です。
適格な移動コンストラクタ
削除されなければ、移動コンストラクタは適格であると言えます。 |
(C++20 まで) |
移動コンストラクタは、
|
(C++20以降) |
適格移動コンストラクタの三値性により、クラスが暗黙的寿命型であるか、およびクラスが三値的にコピー可能型であるかどうかが決定されます。
暗黙的に定義された移動コンストラクタ
暗黙的に宣言された移動コンストラクタが削除もトリビアルでもない場合、odr-used または定数評価で必要ならコンパイラによって定義(つまり、関数本体が生成されてコンパイルされる)される。 論理和型の場合、暗黙のうちに定義された移動コンストラクタはオブジェクト表現をコピーします (std::memmove のように)。 非ユニオンクラス型 (class と struct) の場合、移動コンストラクタはオブジェクトのベースと非スタティックメンバを初期化順に、xvalue 引数による直接初期化を使ってメンバ単位で完全に移動させます。 これが constexpr コンストラクタの要件を満たす場合、生成された移動コンストラクタは constexpr
.
注意事項
強い例外保証を可能にするために、ユーザ定義の移動コンストラクタは例外をスローしてはならない。 例えば、std::vector は要素の再配置が必要な場合、std::move_if_noexcept に依存して move と copy のどちらかを選択するようになっています。
コピーと移動の両方のコンストラクタが提供され、他のコンストラクタが実行可能でない場合、オーバーロード解決は、引数が同じ型の rvalue (std::move の結果などの xvalue または名前のないテンポラリなどの prvalue (C++17 まで)) なら移動コンストラクタを選択し、引数が lvalue (名前のあるオブジェクトまたは lvalue 参照を返す関数 / 演算子) ならコピーコンストラクタを選択します。 コピー コンストラクタのみが提供される場合、すべての引数カテゴリはそれを選択し (rvalues は const 参照にバインドできるので、const 参照を取る限り)、移動が利用できない場合、コピーを移動のフォールバックとする。 それは何かを移動する義務はなく、クラスは移動するリソースを持つ必要はなく、パラメータが const rvalue reference (const T&&) である許容される (しかし多分賢明ではない) ケースのように ‘move コンストラクタ’ はリソースを移動できない可能性があります。
例
#include <string>#include <iostream>#include <iomanip>#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)), // explicit move of a member of class type k(std::exchange(o.k, 0)) // explicit move of a member of non-class type { }}; A f(A a){ return a;} struct B : A{ std::string s2; int n; // implicit move constructor B::(B&&) // calls A's move constructor // calls s2's move constructor // and makes a bitwise copy of n}; struct C : B{ ~C() { } // destructor prevents implicit move constructor C::(C&&)}; struct D : B{ D() { } ~D() { } // destructor would prevent implicit move constructor D::(D&&) D(D&&) = default; // forces a move constructor anyway}; int main(){ std::cout << "Trying to move A\n"; A a1 = f(A()); // return by value move-constructs the target from the function parameter std::cout << "Before move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; A a2 = std::move(a1); // move-constructs from xvalue std::cout << "After move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; std::cout << "Trying to move B\n"; B b1; std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "\n"; B b2 = std::move(b1); // calls implicit move constructor std::cout << "After move, b1.s = " << std::quoted(b1.s) << "\n"; std::cout << "Trying to move C\n"; C c1; C c2 = std::move(c1); // calls copy constructor std::cout << "Trying to move D\n"; D d1; D d2 = std::move(d1);}
Output:
Trying to move ABefore move, a1.s = "test" a1.k = -1After move, a1.s = "" a1.k = 0Trying to move BBefore move, b1.s = "test"After move, b1.s = ""Trying to move Cmove failed!Trying to move D
Defect reports
以下の動作を変える欠陥報告が以前に公開された C++ 標準に遡って適用されています。
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1402 | a defaulted move constructor that would call a non-trivial copy constructor was deleted; 削除されたデフォルトの移動コンストラクタは、 |
オーバーロードの解決に参加し、そのようなコピー コンストラクタの呼び出しを許可していました。 overload resolution |
|
CWG 2094 | C++11 | defaulted move constructor non の volatile subobject を作成した場合。trivial (CWG496) |
trivial not affected |
See also
- converting constructor
- copy assignment
- copy constructor
- コピーエリジョン
- デフォルトコンストラクタ
- デストラクタ
- explicit
- 初期化
- 集合体初期化
- 定数初期化
- コピー初期化
- デフォルト初期化
- 直接初期化
- 初期化リスト
- リスト初期化
- 参照初期化
- 値初期化
- ゼロ初期化
移動代入
- 新規