C++でstd::moveを使うときに気をつけること
std::move は名前のせいで少し誤解しやすいです。
std::move(x) と書いた瞬間に何かが移動するわけではありません。x を「ムーブできる値」として扱えるようにするキャストです。
std::moveの基本
#include <string>
#include <utility>
std::string a = "hello";
std::string b = std::move(a);
この例では、std::move(a) によって a が右辺値として扱われ、std::string のムーブコンストラクタが使われます。
移動処理を実際に行うのは std::string 側です。
move後の値は使えるが中身に期待しない
ムーブ後のオブジェクトは、有効ではあります。
ただし、中身がどうなっているかには期待しない方がよいです。
std::string a = "hello";
std::string b = std::move(a);
// aは有効だが、中身は期待しない
a = "world";
再代入や破棄はできます。ムーブ前の値が残っている前提で読むのは避けます。
constにmoveしても動かないことがある
const な値に std::move しても、ムーブできないことがあります。
const std::string a = "hello";
std::string b = std::move(a);
std::move(a) の型は const std::string&& になります。
多くのムーブコンストラクタは非constの右辺値参照を受け取るため、この場合はコピーになることがあります。
const とムーブは相性がよくありません。
returnで無理にmoveしない
ローカル変数を返すときに、何でも std::move を付ける必要はありません。
std::string makeName() {
std::string name = "hello";
return name;
}
この形では、コピー省略やムーブが効く余地があります。
一方で、次のように書くと最適化を邪魔することがあります。
std::string makeName() {
std::string name = "hello";
return std::move(name);
}
最近のC++では、ローカル変数をそのまま返す形をまず考えます。
unique_ptrでは所有権を移す
std::unique_ptr はコピーできないので、別の所有者へ渡すときに std::move が必要です。
auto ptr = std::make_unique<int>(1);
auto other = std::move(ptr);
この後、ptr は空になります。
unique_ptr と shared_ptr の使い分けは C++でunique_ptrとshared_ptrを使い分ける目安 に分けています。
まとめ
std::move は移動そのものではなく、ムーブできる形へ変換するものです。
std::move自体はキャスト- 実際に移動するかは受け取る側次第
- move後の値は有効だが中身には期待しない
constな値はムーブできないことがある- returnでは無理に
std::moveを付けない
使う場面を絞ると、std::move はかなり読みやすくなります。


