std::moveの仕組み

2014年9月21日日曜日

プログラム

t f B! P L
こないだからC++11づいてますが、std::moveの定義内容の説明でも。
template<typename _Tp>
  constexpr typename std::remove_reference<_Tp>::type&&
  move(_Tp&& __t) noexcept
  { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

この関数の機能は、rvalueへのキャスト、つまり破壊していいよというマークをつけることです。

やっている内容は引数を参照で受け取り(つまりコピーは発生しません)、
さらに受け取った物をrvalue参照で返す(こちらもコピーは発生しません)
ということです。

ただ、その過程で戻り値がrvalue参照になるため、コンパイラは、その値を壊していいと扱うわけです。

例えばこのstd::move関数にlvalueを渡して、
std::string hello("hello");
std::string a = std::move(hello);
std::cout << hello << std::endl;
なんてすると、何も表示されません。
lvalueの変数helloは破壊されてしまいます。
気をつけましょう。



以下、もうちょっと詳しい内容
str = std::move(std::string("hello"));
こうやってrvalueを渡すと、
constexpr std::string&&
move(std::string&& __t) noexcept
{ return static_cast<std::string&&>(__t); }
と動作します。やってることは、rvalue参照をrvalue参照にキャストして返します。
つまり、何もしません。一方、
std::string hello("hello");
str = std::move(hello);
のように上記の例と同様にlvalueを渡すと、
constexpr std::string&&
move(std::string& __t) noexcept
{ return static_cast<std::string&&>(__t); }
と動作します。その理由は、std::string& &&のように使うと、
reference collapsingが起きて、std::string&と扱われるからです。
やってることは、lvalue参照をrvalue参照にキャストして返します。
つまり、破壊していいとマークを付けている訳です。

ここで、こんなコードを書くと、どちらが呼ばれるでしょうか?
void setter1(std::string&& name)
{
  name_ = std::move(name);
}
void setter2(std::string name)
{
  name_ = std::move(name);
}
どちらの関数とも、lvalue参照のstd::moveを呼び出します。
どちらも、setterを呼び出す側から見るとrvalueを渡しているのですが、
setter関数内部からは、もう変数のアドレスが指すメモリ上の値なので、
lvalueとして扱われます。

つい、見た目でrvalueなんて勘違いしそうですが、注意しましょう(自戒)。

最後に、
void setter3(const std::string& name)
{
  name_ = std::move(name);
}
とすると、どうなるでしょう?

この場合、
constexpr const std::string&&
move(const std::string& __t) noexcept
{ return static_cast<const std::string&&>(__t); }
となります。一見rvalue参照にキャストされたように見えますが、
rvalue参照の戻り値はrvalueとして扱いますので、
const std::string&&はconst std::stringとして扱われます。
そして、const付きrvalueはlvalueですので、次の代入の=では、
move assign operatorではなく、assign operatorが呼び出されます。
結果として、const std::stringの中身は、破壊されることなくコピーされるというわけです。

色々難しいですね。
性能を上げようと、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