C++ と const 参照
数年前,まだ私が C++ でゲームを書いていたころ,深すぎる関数のネストは「一旦変数に入れ」て「読みやすくする」というコーディングスタイルにだいたい落ち着いていた.もちろん,「これは一旦別名を付けるだけで,変数に再代入する気はないよ」ということで const も付ける.ただあまり大きなデータのコピーは嫌だよねということで,戻り値が std::string や構造体の関数呼び出しを「一旦変数に入れる」ときは const 参照を好んで使っていた.
A(B(C(a), b, D(E(c), d, e)));
「む,なんて読みにくいコード.ばらせよ」
const int target_id = C(a); const MessageBody& message_body = D(E(c), d, e); const Message& msg = B(target_id, b, message_body); A(msg);
とかなんとか.
んで,次のような振る舞いをすっかり忘れているわけだ.
#include <iostream> #include <vector> void foo(const std::vector<int>& src, std::vector<int>& dest) { const int& cached_ref = src[0]; const int cached_copy = src[0]; std::cout << "cached_ref: " << cached_ref << std::endl; std::cout << "cached_copy: " << cached_copy << std::endl; dest[0] = 2; std::cout << "cached_ref: " << cached_ref << std::endl; std::cout << "cached_copy: " << cached_copy << std::endl; } int main() { std::vector<int> v; v.push_back(1); foo(v, v); return 0; } /* result cached_ref: 1 cached_copy: 1 cached_ref: 2 cached_copy: 1 */
const 参照は,参照先の内容まで immutable だとは言っていない.たとえるならファイルをリードオンリーでメモリマップしたようなものだ.誰かがリードライトで同じファイルを開いて書き込めば,当然中身は変わっている.const 参照の意味は「この変数を通しては参照先を書き換えませんよ」であって,「この変数の中身は不変ですよ」ではない.
少しひねってみる.
class Data { std::string m_string; public: void Deserialize(const std::string& str) { m_string = str; } const std::string& SerializeAsString() const { return m_string; } }; void bar(const Data& src, Data& dest) { const std::string& serialized = src.SerializeAsString(); std::cout << serialized << std::endl; // 長い処理 dest.Deserialize( "hauhau" ); // 長い処理 std::cout << serialized << std::endl; }
関数内で「内容が不変なキャッシュ」のように使われている const 参照があったとき,考えるべきことは色々ある.確かにその参照は,初期化時と同じオブジェクトを指し続けてはいるだろうが,その中身まで同じとは限らない.