Code Fix

中級

範囲for文で要素をコピーしてしまい変更が反映されないバグ

for (int n : nums) のように範囲for文の変数を値(コピー)で受け取っているため、ループの中でnを書き換えても元のvectorの要素は変わらないバグです。

なぜエラーが出ないのか

出力: 1 2 3
(エラーなし)
C++は何も報告しません。コンパイルも実行も正常に完了するためです
出力: 1 2 3
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> nums = {1, 2, 3};
 5      for (int       n : nums) {
                 ^
 6          n *= 2;
 7      }
 8      for (int n : nums) std::cout << n << " ";
 9      std::cout << std::endl;
10      return 0;
11  }
出力: 1 2 3期待: 2 4 6

for (int n : nums)は各要素をnという新しい変数にコピーしているだけです。nを書き換えても元のvectorの要素には影響しません。直接書き換えるにはint&で受け取ります。

直し方: int を int& にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> prices = {10, 20, 30};
 5      for (int       p : prices) {
                 ^
 6          p += 5;
 7      }
 8      for (int p : prices) std::cout << p << " ";
 9      std::cout << std::endl;
10      return 0;
11  }
出力: 10 20 30期待: 15 25 35

範囲for文はコンテナの要素を直接触っているように見えますが、値で受け取っている限りコピーへの変更にしかなりません。

直し方: int を int& にします。

この問題を解いてみる →

パターン3

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> scores = {5, 6, 7};
 5      for (int       s : scores) {
                 ^
 6          s = 0;
 7      }
 8      for (int s : scores) std::cout << s << " ";
 9      std::cout << std::endl;
10      return 0;
11  }
出力: 5 6 7期待: 0 0 0

全要素をリセットしたいのにint&ではなくintで受け取ると、コピーへの代入で終わってしまい元のvectorは一切変わりません。

直し方: int を int& にします。

この問題を解いてみる →

パターン4

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> vals = {2, 4, 6};
 5      for (int       v : vals) {
                 ^
 6          v += 1;
 7      }
 8      for (int v : vals) std::cout << v << " ";
 9      std::cout << std::endl;
10      return 0;
11  }
出力: 2 4 6期待: 3 5 7

値で受け取ると各要素はコピーされ、変更しても元のvectorには反映されません。

直し方: int を int& にします。

この問題を解いてみる →

パターン5

 1  #include <iostream>
 2  #include <vector>
 3  int main() {
 4      std::vector<int> weights = {1, 1, 1};
 5      for (int       w : weights) {
                 ^
 6          w = 9;
 7      }
 8      for (int w : weights) std::cout << w << " ";
 9      std::cout << std::endl;
10      return 0;
11  }
出力: 1 1 1期待: 9 9 9

全要素を書き換えたい場合はint&で受け取る必要があります。

直し方: int を int& にします。

この問題を解いてみる →

よくある誤解

「範囲for文はコンテナの要素を直接触っている」というのは誤解です。for (int n : nums) は各要素をnという新しい変数にコピーしているだけで、nへの変更は元の要素に影響しません。直接書き換えたいときは for (int& n : nums) のように参照で受け取る必要があります。

実務での勘所

変更する意図が無い場合でも、値渡し(for (std::string s : names))は要素をコピーするコストがかかります。std::stringのような、中身の複製に相応のコストがかかる型を大量に走査する場面では、for (const std::string& s : names)のように定数参照で受け取れば、コピーを避けつつ誤って書き換えることも防げます。変更する場合はauto&、変更せずコピーも避けたい場合はconst auto&、要素が小さいプリミティブ型でコピーのコストが気にならない場合だけ値渡し、という使い分けが実務の目安です。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)