リファレンスの戻り値がいつの間にか壊れちゃう問題について


C++を書いててやらかすミスで精神的ダメージが大きいのは、スタック上の値をリファレンスとして返してしまい、参照先の値が壊れることだ。

これは極めて初歩的なミスであるゆえに痛いわけだが、込み入ったケースではついやらかしてしまう場合がある。というか、俺はやってしまった。込み入ったケースというのは、スタック上の値を別の関数に渡し、その関数が受け取った値から一部を抜き出しリファレンスとして返したのをそのまま戻り値として返してしまうというようなケースだ。

const Fuga& get_fuga (const Hoge& hoge)
{
    return hoge.fuga;
} 

const Fuga& get_fuga (const Buhi& buhi)
{
    if (...)
        return buhi.get_fuga();
    else
        return get_fuga (Hoge (...));
}

いや、この例だと伝わんないかもしれないけど、とにかく込み入っていたんだ。

特に問題なのが、再帰呼び出しとかでスタックの割と深いところにある値をリファレンスで返してしまった場合。この場合、同じくらい深いところまでスタックが消費されない限り、戻り値は正しい値として生き続ける。結果、単体テストを通過してしまい、運用テストとかの段階になって全く関係のないようなところで値が壊れるというようなことが起こり得る。

俺は今までこの問題はとにかくきおつけるしかないのかと思っていたのだけど、突然いいことを思いついた。単体テストで戻り値を調べるときに、あらかじめスタックをランダムな値で壊しとけばいいんでない?

template<class T, int S=4096>
bool is_equal (const T& v1, const T& v2)
{
    int puff [S];
    for (int i=0; i<S; i++)
        puff [i] = rand();
    return v1 == v2;
}

こんな感じで。これをスタック破壊検査と名付けよう。なんちてw

※コメント欄でご指摘を受けたように、このコードは最適化を無効にしてコンパイルしないと意味ない。

いじょう