参照渡しのつもりが値渡しで変更が反映されないバグ
関数の引数を値渡し(int n)で受け取っているのに、関数の中身を書き換えたつもりで呼び出し元の変数まで変わることを期待してしまうバグです。値渡しでは引数はコピーされるため、関数内での変更は呼び出し元に影響しません。
なぜエラーが出ないのか
出力: 5(変更されていない)
(エラーなし)- C++は何も報告しません。コンパイルも実行も正常に完了するためです
出力: 5(変更されていない)- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 #include <iostream> 2 void increase(int n) { ^ 3 n = n + 1; 4 } 5 int main() { 6 int count = 5; 7 increase(count); 8 std::cout << count << std::endl; 9 return 0; 10 }
値渡し(int n)では引数はコピーされるため、関数内でのnへの変更はcountに影響しません。呼び出し元を変更したいなら参照渡し(int&)にします。
直し方: int を int& にします。
パターン2
1 #include <iostream> 2 void doubleIt(int n) { ^ 3 n = n * 2; 4 } 5 int main() { 6 int x = 4; 7 doubleIt(x); 8 std::cout << x << std::endl; 9 return 0; 10 }
関数の中で「変数そのものを書き換えている」つもりでも、値渡しでは別の実体(コピー)を書き換えているだけです。
直し方: int を int& にします。
パターン3
1 #include <iostream> 2 void reset(int n) { ^ 3 n = 0; 4 } 5 int main() { 6 int score = 77; 7 reset(score); 8 std::cout << score << std::endl; 9 return 0; 10 }
呼び出し元の変数を関数から変更したいときは、引数の型を参照(&)にする必要があります。
直し方: int を int& にします。
パターン4
1 #include <iostream> 2 void addTen(int n) { ^ 3 n = n + 10; 4 } 5 int main() { 6 int total = 3; 7 addTen(total); 8 std::cout << total << std::endl; 9 return 0; 10 }
値渡しでは引数はコピーされるため、関数内での変更は呼び出し元のtotalに影響しません。
直し方: int を int& にします。
パターン5
1 #include <iostream> 2 void clearIt(int n) { ^ 3 n = 0; 4 } 5 int main() { 6 int val = 9; 7 clearIt(val); 8 std::cout << val << std::endl; 9 return 0; 10 }
呼び出し元の変数を変更したい場合は、引数の型を参照(&)にする必要があります。
直し方: int を int& にします。
よくある誤解
「関数に変数を渡せば、その変数そのものが渡される」というのは誤解です。値渡しでは変数の値のコピーが新しい引数として作られるだけで、呼び出し元の変数とは別の実体になります。変更を反映したいなら参照渡し(int&)が必要です。
実務での勘所
呼び出し元の値を変更したい場合、C++には参照渡し(int&)とポインタ渡し(int*)という2つの手段があります。実務での使い分けの目安は、「必ず有効な変数を渡す」場面では参照渡し、「値が無いかもしれない(nullptrを渡す可能性がある)」場面ではポインタ渡し、というものです。参照はnullptrに相当する状態を表現できないため、省略可能な引数にはそもそも使えません。