p_tan's blog

勉強日記です。ツッコミ大歓迎

const参照引数でハマった

const参照の引数でうっかりしてハマったこと。

以下のような、レンジの全ての要素から、一括してある値を引くようなヘルパ関数を作っていた。
(実際は足し算や掛け算などもサポートするように汎用的に書いていたが)

template<class T, class Range>
void Sub(Range &r, const T& value){
    for(auto& elem : r){
        elem -= value;
    }
}

int main(){
    int data[] = {1, 2, 3};
    Sub(data, 1);  // -> data = {0, 1, 2} 便利!
}

さて、配列の最小値が0になるように値をシフトさせようと思って、ヘルパ関数を以下のように使用したら意図した結果にならなかった。

#include <algorithm>
#include <iterator>

int main(){
    int data[] = {1, 2, 3};
    Sub(data, *std::min_element(std::begin(data), std::end(data)));  // -> data = {0, 1, 2} ではなく, {0, 2, 3} になる???
}

問題は、関数Subの引数valueに、第一引数rの要素への参照が渡っているため、const参照であるにも関わらず、関数の実行中にvalueの値が変化したことにある。
参照によるオブジェクトの共有の問題は、頭では理解していても、うっかりでバグを埋め込む可能性が高い。しかも非常に見つけづらいバグなので厄介だ。