move semanticsとrvalue参照

2014年9月6日土曜日

プログラム

t f B! P L
ところで、std::moveを使うだけで、どうして「移動」ができるんでしょうね?
std::string name;
name = std::move("hello");
C++98では、=は代入、つまりコピーでした。
つまり、std::moveだけでは「移動」できる訳がありません。

「移動」するための仕組みは、rvalue参照にあります。

std::moveは、実は実装としては何もしません。
この関数は、rvalue参照の引数を取り、それをrvalue参照に型変換するだけです。
この型変換によって、テンプレートの何処かでlvalueが紛れ込んでいないことを、
コンパイル時に確認します。
この型変換によって、何でも破壊可能なrvalueに変換します。
間違えてlvalue参照を渡すと破壊されるので注意が必要です。

実際の「移動」は、rvalue参照を引数にしたstd::string用の=オペレータが処理します。
=オペレータが、rvalueを参照渡しで受け取り、swapして内容を入れ替えます。
std::string&
operator=(std::string&& str)
{
    this->swap(str);
    return *this;
}
この、引数がrvalue参照の=オペレータを、
ムーブ代入オペレータ(move assignment operator)と呼びます。
これまでの引数がconst参照の=オペレータは、
これまで通り代入オペレータ(assignment operator)と呼びます。

しかし、なぜこのムーブ代入オペレータは「移動」するためのswapを使う
コードを記述できたのでしょうか?

それは、rvalue参照渡しのデータは、呼ばれ側が、
自由に壊して良いというルールが存在するからです。

そのため、上記の通りswapを使った、破壊型ですが、
メモリコピーの必要ない高速なコードを書くことができました。

C++11の実装では、std::stringやstd::vectorなど、標準ライブラリ全てに対し、
rvalue参照を取る新しいオペレータや関数を用意して、上記のようにswapを使うなどして、
効率化を向上し、高速化を達成しました。

その結果、std::moveが型変換しかしなくても、「移動」が実現されたというわけです。
これが、moveがsyntaxではなく、semanticsな理由でもあります。
素晴らしい、さあ、窓からC++98を投げ捨(ry

補足: もちろん、std::move("hello")の所も、そのままだとコピーが発生します。
そこで、std::move自体もrvalueの参照渡しで引数を受け取っています。

ラベル

AndroidTV (1) chromebook (2) DIY (4) docker (1) git (4) Ingress (4) llvm (3) MacBook (1) MVNO (1) narou (1) PS4 (2) QNAPNAS (9) SIMD (9) SmartBand (8) Ubuntu (9) VAIO (1) Windows (2) wsl (2) wsl2 (1) Xperia (20) トルネ (3) プログラム (26) ルーター (18) 音楽 (6) 家事 (2) 自炊 (2) 電子書籍 (2) 洋食 (4)

フォロワー

QooQ