ゼロエックスって書くと、
新しいガンダムかと思いそうですが、C++ 0xの話です。

またプログラム知らない人にはごめんなさいな記事だよ!


C++ 0xにて色々な強化がされました。
コンセプト、ラムダ式、constexprなど
大きなものから小さなものまで。
コンセプトさんは死んじゃったけどね・・・。

で、その中の一つに「右辺値参照」があります。


んで、この「右辺値参照」の説明をしようと思ったんですが、
左辺値と右辺値ってなーに?ってところから
まともに詳しく書いていくとあまりに長く難解になりそうなんで
語弊を含む部分もあるかもしれませんが、省略して書いていきます。


左辺値と右辺値の違いは、式の終了後も
生存するオブジェクトかどうかです。
(左辺値=非一時オブジェクト、右辺値=一時オブジェクト)

int x = 1;

という式が存在した場合、
xは式の終了後も存続するオブジェクトですが、
右側の1は、式の終了時に破棄される一時オブジェクトです。

その為、上記の式の場合、xが左辺値、1が右辺値になります。


つまり、左辺値はいつでも見れる保存したエロ画像で、
右辺値は街中で見かけた偶然のパンチラみたいなものです。

int x = 1;
という式は偶然のパンチラを、咄嗟にケータイで撮って、
xというエロ画像として保存するという式なのです。

まったく女性の敵のような式ですね!
紳士的な俺は怒りを隠せません!ビキビキ


さて、非一時オブジェクトと一時オブジェクトに関して
次のような違いがあります。

void megane(int& aInt);

と言ったように、非constの参照を受け取るメソッドに
・非一時オブジェクトは渡すことができる
・一時オブジェクトは渡すことができない
というルールがあるのです。

エロ画像を他人に渡すことはできても、パンチラの記憶は
他人に渡せないのと一緒です。


で、上記のルールによって、

megane(1);

というような式は書けず、

int x = 1; ①
megane(x);

というような式を書く必要が出てきます。
折角コピーのオーバーヘッドをなくしたくて、参照にしても
実質一度ローカル変数に取らなければいけないのであれば、
①の部分で余計なコピーが発生しているのです。


で、右辺値参照というのは、
そのコピーを減らそうぜというもの。

void megane(int&& aInt);

とした場合、

megane(1);

を行うことが可能であり、また引数は値渡しではないので
余計なコピーは発生しないというものです。

ここでは説明しませんが、右辺値参照後の定石となりそうな
むーぶコンストラクタを実装することで、またさらに右辺値参照が
強力になったりします。

それはまた別の機会に。

※const参照ではなく、右辺値参照による利点は
 まさにムーブコンストラクタで出てくるのですが、
 高校の時の彼女が巨乳だったことを思い出したので、
 それどころではなくなりました。
お久しぶりです。五日ぶりに家に帰れたぞ!
ということで、プログラミングの話。
プログラミングの知識がないかたはごめんなさい。

で、プログラムというのは人から人に
渡っていく機会の多いものです。

少なくとも、一つのプログラムを
最後まで一人だけしか触らないということは、
お仕事においては、多くありません。多分ね。

その為、可読性の高いコードを書く技術が
必要になってきます。

今日はその中の一つのお話で、
読むのを面倒くさくするより、書くのを面倒くさくしようぜ。
というお話。
※個人的な好みも含んでいます。



例えばの話、配列のような動きをするクラスを作ります。
C++で書いてみましょう。
テンプレートの勉強ではないので、unsigned char型の
要素数固定配列とします。

class MeganeArray
{
private:
  unsigned char mBody[256];
  int mLength;

public:
  int Append(const MeganeArray& iSrc);
  MeganeArray& operator+(const MeganeArray& iSrc);
};


とりあえず、コンストラクタとかoperator[]は省略して、
お話に必要な部分だけ記載。
上記のようなクラスがあったとしましょう。

で、ここで気になるのがAppendとoperator+です。
説明するまでも無いとは思いますが、

int Append(const MeganeArray& iSrc)
 引数としてMeganeArrayの参照を受け取り、
 現在のmBodyのmLength以降の部分に
 引数の内容を追加するメソッド。
 戻り値はエラーコード。

MeganeArray& operator+(const MeganeArray& iSrc);
 引数としてMeganeArrayの参照を受け取り、
 現在のmBodyのmLength以降の部分に
 引数の内容を追加するメソッド。
 戻り値は自身の参照。


というメソッドとして定義しています。
ぶっちゃけ上記のようなoperator+は、
「要素数を超えた量を追加しようとする可能性が
 operator+のコール以前に潰されている必要性がある」
とか色々問題がありますが、
実際に書く人がいるのでこれでいきます。



でですね。
この定義をすると、確かに書きやすいです。
Append、operator+、どちらも有用に使える場面があります。
(ケツが切れても問題ない配列の追加なら、
 戻り値でエラーコードを貰ってのエラー処理は、
 無視することも可能は可能)

ただ、ぶっちゃけるとやってることは同じなんです。
自身のケツに、もらったものを付け加えてるだけです。アッー!

なら、Appendだけで良くないだろうかと思います。

同じことをやっていて、引数も同じなのに、
ただ単に

MeganeArray array;
if (0 == array.Append(arraySrc1)) { return FALSE; }
if (0 == array.Append(arraySrc2)) { return FALSE; }

みたいに書くのが面倒くさくて、

MeganeArray array;
array = arraySrc1 + arraySrc2;

みたいな書き方をしたいから、operator+やoperator=を
オーバーロードするのは、一見、可読性が高まるように見えます。

ですが、実際は
・operator+はAppendに失敗した場合、どういった挙動をするのか。
・operator+で追加している場面は、本当に要素数内で収まる
 追加であるのか。
 もし要素を超える場合、超えても本当に問題がないのか。
などの部分がややこしくなってるだけな気がします。

特にoperator=なんかは良くオーバーロードされている気がしますが、
・operator=はコピーしているのかアドレスだけ受け取っているのか。
という部分を理解せずに使えば、実行時エラーをぽこじゃかと引き起こします。

結局、あとから来た人の読まなきゃいけない部分が増えてるだけです。



また、
「Append、operator+、どちらも有用に使える場面があります」
と書きましたが、どういう場面で有用かを理解せずに
メソッドを使用するプログラマも、残念ながら少なくありません。

直感的に使用できるメソッドを用意した場合、
直感だけで使用される危険性もはらんでいます。



C++はオペレータのオーバーロードを使うことによって、
直感的なコードを書けるようになっています。
ただ、読む人はやっぱりきちんと理解しないといけない部分です。

ユーティリティとしては利便性を求めるのも大事ですが、
あとから来た人はそのクラスがどういう設計で作られているかを
知ることからはじめなければいけません。


知っている前提での利便性は、コードの毒になりかねないのでは。
と個人的には思うのでした。
河童に
「おまえ、どこ中だよ?」
って絡まれる夢を見ました。

お前こそ、どこ中だよ…。