コンテンツにスキップ

最終更新:2026-10-12

C++ std::move の原理と使い方

std::move は非常に重要な関数であり、左値オブジェクトを xvalue という特殊な型の右値オブジェクトとしてマークする方法を提供します。この変換は、通常のコピーではなくムーブセマンティクスを実行できるようにするためのものです。

  • なぜ std::move を使うのか
  • 実際の使用例

1.なぜ std::move を使うのか

通常、ムーブ操作を行えるのは右値オブジェクトだけです。ムーブコンストラクタは右値参照をパラメータとして使い、右値参照は右値にしか束縛できないからです。したがって、左値式で表されるオブジェクトからムーブ操作を実行したい場合は、std::move を使ってそれを xvalue(特殊な右値)に変換する必要があります。

std::move を使う際は注意が必要です。ムーブ操作はオブジェクトを、通常は破棄しか許されない状態に置きます。そのため、オブジェクトの最後の使用時に対してのみムーブ操作を行うのがよいでしょう。

std::move は実際にはムーブを実行しません。明確にしておくと、std::move はオブジェクトのムーブ操作を実際には実行しません。与えられた左値オブジェクトを右値参照に変換するだけです。以下は std::move の可能な実装の1つです:

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
    return static_cast<typename remove_reference<T>::type&&>(t);
}

2.実際の使用例

2.1 コピー構築ではなくムーブ構築

関数 f1 の中では、パラメータ a は右値ではなく、名前を持っているため右値参照の左値です。したがって、Foo のムーブコンストラクタを強制的に実行するには、それを右値に変換する必要があり、std::move まさにそれを完璧に行ってくれます。

struct Foo { 
    ... 
    Foo(const Foo&) {} 
    Foo(Foo&&) {} 
}; 

void f1(Foo&& a) { 
    Foo b(a); // 调用拷贝构造函数,而非移动构造函数
} 

void f2(Foo&& a) { 
    Foo b(std::move(a)); // right
} 

2.2 所有権管理

次のようなコードを想定します:

Foo* p = new Foo();
data.push_back(p);

上のコードは正常に動作しますが、ポインタの所有権がどこにあるのかを確認できません。誰がポインタを削除するのでしょうか?あるいはポインタはいつ削除されるべきなのでしょうか?コンテナが破棄されるときに削除されるのでしょうか?

C++11 では、std::unique_ptr<Foo> p(new Foo()); という文はコンパイルエラーになります。理由は、唯一所有権のルールが適用されるためです。

しかし、std::move を使ってポインタの所有権を移すことができます:

data.push_back(std::move(p));

これで、ポインタの所有権はコンテナに移り、コンテナのライフタイムの終わりに削除されます。オブジェクト p は依然として存在しますが、それを逆参照しようとする試みはすべてヌルポインタ例外を引き起こします。

実際、ムーブ操作の後、p が持っていた内部ポインタは nullptr に設定されています。

以上のように、std::move の役割は、オブジェクトのムーブセマンティクスを安全に実現する手段を提供することにあります。左値としての性質を右値へと変え、ムーブコンストラクタやムーブ代入演算子から呼び出せるようにするのです。std::move を正しく使うことで、性能を向上させ、正しい所有権の移転を保証できます。

では次の疑問ですが、いつ std::move が不要なのでしょうか?

関連トピック

コメント