2017年09月07日

ランボー/怒りの右辺値参照

どうも。
ピッチピチの新技術よりも、カッスカスの枯れた技術が好きなぷーすけです。

そうは言っても、C++も年を追うごとに仕様が新しくなってきて
隔世の感がありまして。いろいろ眺めてみました。

で、今更ですが、右辺値参照というものを
飲み込むのにえらい苦労しまして、怒りを込めて忘備録代わりに筆を執りました。


【右辺値とは何ぞや】

巷では「優性遺伝」「劣性遺伝」の呼称が変わるとかで結構なことです。
まぁあれは嘘も方便と申しますか、
理解の手助けにはなるかと勝手に思っておりますが、
「右辺値」というのはむしろ理解を遠ざけると感じざるを得ません。
YE GUILTY
x = 0;
xが左辺値で、0が右辺値。確かにそうだ。
しかしプログラミングは通常、代入だけで
構成されているものではありません。

右辺値参照における右辺値とは
「左辺不可能値」と表現した方が近いと思います。
0 = x;
こんな式、不可能ですな。意味わかんない。もぅマヂ無理。
だから0は右辺値(左辺不可能値)。


【参照についておさらい】

右辺値参照で悩む方には失礼な記事かと思いますが、
一応書き込んでおきます。

void get_everything(int &answer){
  answer = 42;
}
int main(){
  int ans;
  get_everything(ans);
  return 0;
}
ansには42が入ります。
これで今日から全時代および全世界において2番目に凄いコンピュータ。


【本題】

右辺値とは左辺不可能値だと書きました。関数の戻り値もそうです。
int twoplustwo(){
  return 2 + 2;
}

int main(){
  twoplustwo() = 5;
  return 0;
}

twoplustwo()は5になりません。4です。オセアニアじゃぁ常識かもしれませんが。

int twoplustwo(){
  return 2 + 2;
}
void get_everything(int &answer){
  answer = 42;
}
int main(){
  get_everything(twoplustwo());
  return 0;
}
このコードはコンパイルが通りません。
get_everything()がtwoplustwo()という右辺値(左辺不可能値)を
参照しているからです。これを合法化するのが右辺値参照。


【それの何が嬉しいの?】

int main(){
  int ans;
  ans = twoplustwo();
  get_everything(ans);
  return 0;
}
さっきのコードを無理やり通そうとするとこんな感じになります。
twoplustwo()の件が無意味ですが。
このコードはint型なので、たいして問題ではありません。
でもこれがstringとか大きなvectorとかだったら?
戻り値のためのオブジェクトを作ってどこかにコピーして破棄。
一時的な変数で戻り値を受けて、どこかにコピーした後すぐに破棄。
まさに無駄地獄。
phpとかjavascriptとかだったら、割と平気にバカスカコピーしてますが、
高貴なるC++プログラマーならその心理的抵抗は大きい。
そもそも今日日C++で何か作るとなったら、それなりの事情があるもんですし。

つまり右辺値参照とは、
「関数の戻り値のような左辺不可能値を参照できるようにして
 コピーのコストを減らしましょう」

というワザです。
stlのコンテナなんかはこれに対応しているようです。良かった。

それがmoveコンストラクタとかそういう話になるんですが、
私もそれ以上解説できる自信もないし気持ちは落ち着いたし眠いし疲れたし
詳しい情報は他所でお願いしますorz
posted by ぷーすけ at 22:37| Comment(0) | TrackBack(0) | プログラミング