ムーブコンストラクタ
クラス T
のムーブコンストラクタは、第1引数が T&&、 const T&&、 volatile T&& または const volatile T&& であって、他の引数がないか残りの引数すべてがデフォルト値を持つ、非テンプレートコンストラクタです。
目次 |
[編集] 構文
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 は現在のクラス (またはクラステンプレートの現在の実体化) を表さなければなりません。 または、名前空間スコープまたはフレンド宣言で宣言されたときは、修飾されたクラス名でなければなりません。
[編集] 説明
- ムーブコンストラクタの一般的な宣言。
- ムーブコンストラクタを強制的にコンパイラに生成させます。
- ムーブコンストラクタの暗黙の生成を回避します。
ムーブコンストラクタは一般的にはオブジェクトが同じ型の右辺値 (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) です。 - 関数の戻り。 T f() のような関数の内部における return a;、ただし
a
はT
型であり、T
はムーブコンストラクタを持つ。
初期化子が prvalue のとき、ムーブコンストラクタ呼び出しはしばしば最適化によって除去されます (C++17未満)行われることはありません (C++17以上)。 コピー省略を参照してください。
ムーブコンストラクタは一般的には引数が保持しているリソース (例えば動的に確保されたオブジェクト、ファイルディスクリプタ、 TCP ソケット、入出力ストリーム、実行中のスレッド、など) をコピーするのではなく「盗み」、引数を何らかの有効だけれどもそれ以外の点では不定な状態に置きます。 例えば、 std::string や std::vector からのムーブは引数を空の状態にするかもしれません。 しかしこの動作に依存するべきではありません。 型によっては (std::unique_ptr など)、ムーブ後の状態が完全に規定されています。
[編集] 暗黙に宣言されたムーブコンストラクタ
クラス型 (struct、 class または union) に対してユーザ定義されたムーブコンストラクタが提供されず、以下のすべてが真の場合、
- ユーザ宣言されたコピーコンストラクタがない。
- ユーザ宣言されたコピー代入演算子がない。
- ユーザ宣言されたムーブ代入演算子がない。
- ユーザ宣言されたデストラクタがない。
|
(C++14未満) |
コンパイラはムーブコンストラクタをそのクラスの非 explicit かつ inline public
なメンバとしてシグネチャ T::T(T&&)
で宣言します。
クラスは複数のムーブコンストラクタ、例えば T::T(const T&&) と T::T(T&&) の両方を持つことができます。 何らかのユーザ定義ムーブコンストラクタが存在する場合でも、ユーザはキーワード default
を用いて暗黙に宣言されたムーブコンストラクタの生成を強制できます。
暗黙に宣言された (または最初の宣言においてデフォルト化された) ムーブコンストラクタは、動的例外指定 (C++17未満)例外指定 (C++17以上)で説明されている通りの例外指定を持ちます。
[編集] 削除された暗黙に宣言されたムーブコンストラクタ
以下のいずれかが真の場合、クラス T
に対する暗黙に宣言されたまたはデフォルト化されたムーブコンストラクタは削除されたものとして定義されます。
-
T
がムーブできない (削除された、アクセス不可能な、または曖昧なムーブコンストラクタ持つ) 非静的データメンバを持つ。 -
T
がムーブできない (削除された、アクセス不可能な、または曖昧なムーブコンストラクタ持つ) 直接または仮想の基底クラスを持つ。 -
T
が削除されたまたはアクセス不可能なデストラクタを持つ直接または仮想の基底クラスを持つ。 -
T
が union ライクなクラスであり、非トリビアルなムーブコンストラクタを持つ変種メンバ持つ。
|
(C++14未満) |
削除された暗黙に宣言されたムーブコンストラクタはオーバーロード解決では無視されます (そうでなければ右辺値からのコピー初期化が妨げられるでしょう)。 |
(C++14以上) |
[編集] トリビアルなムーブコンストラクタ
以下の条件がすべて真の場合、クラス T
に対するムーブコンストラクタはトリビアルです。
- ユーザ提供されない (つまり暗黙に定義されたまたはデフォルト化された)。
-
T
が仮想メンバ関数を持たない。 -
T
が仮想基底クラスを持たない。 -
T
のすべての直接の基底について、選択されたムーブコンストラクタがトリビアルである。 -
T
のすべてのクラス型 (またはクラスの配列型) の非静的メンバについて、選択されたムーブコンストラクタがトリビアルである。
|
(C++14以上) |
トリビアルなムーブコンストラクタはトリビアルなコピーコンストラクタと同じ動作を行うコンストラクタです。 つまり、 std::memmove によって行われたかのように、オブジェクト表現のコピーを行います。 C 言語と互換性のあるすべてのデータ型 (POD 型) はトリビアルにムーブ可能です。
[編集] 暗黙に定義されたムーブコンストラクタ
暗黙に宣言されたムーブコンストラクタが削除されておらずトリビアルでもない場合は、 ODR 使用された場合、コンパイラによって定義されます (つまり、関数の本体が生成され、コンパイルされます)。 非 union クラス型 (class および struct) の場合、ムーブコンストラクタはそのオブジェクトの基底と非静的メンバの完全なメンバ単位のムーブを、その初期化の順序で、 xvalue 引数による直接初期化を用いて行います。 これが constexpr コンストラクタの要件を満たす場合、生成されるムーブコンストラクタは constexpr
です。
[編集] ノート
強い例外保証を可能とするために、ユーザ定義されたムーブコンストラクタは例外を投げるべきではありません。 例えば、 std::vector は要素を再配置する必要があるときにムーブとコピーの間で選択するために std::move_if_noexcept を頼ります。
コピーとムーブ両方のコンストラクタが提供され、他のコンストラクタが可視でない場合、オーバーロード解決は引数が同じ型の右辺値 (std::move の結果などの xvalue または名前のない一時オブジェクトなどの prvalue (C++17未満)) であればムーブコンストラクタを選択し、引数が左辺値 (名前付きのオブジェクトまたは左辺値参照を返す関数/演算子) であればコピーコンストラクタを選択します。 コピーコンストラクタのみが提供されている場合は、すべての引数カテゴリがそれを選択します (const への参照を取る限り (右辺値は const 参照に束縛できるため))。 これは、ムーブが利用可能でないときに、コピーをムーブのフォールバックにします。
コンストラクタは、引数として右辺値参照を取る場合、「ムーブコンストラクタ」と呼ばれます。 ムーブコンストラクタは何もムーブする義務はなく、クラスはムーブするリソースを持つことは要求されず、「ムーブコンストラクタ」は引数が const 右辺値参照 (const T&&) である (実用的ではないかもしれませんが許されています) 場合のようにリソースをムーブできないことがあります。
[編集] 例
#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)), // クラス型のメンバの明示的なムーブ。 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 << "Trying 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 << "Trying to move C\n"; C c1; C c2 = std::move(c1); // コピーコンストラクタを呼びます。 std::cout << "Trying 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