优先用 for (const auto& x : container) 读取不修改,for (auto& x : container) 修改元素,大对象避免拷贝;map 遍历用 p.first/p.second 或 c++17 结构化绑定;非标准容器需支持 begin/end;循环中增删容器会致迭代器失效崩溃。

什么时候该用 for (auto& x : container) 而不是 for (auto x : container)
拷贝值还是引用,直接决定性能和行为是否符合预期。对 std::string、std::vector 这类可能较大的类型,按值遍历会触发不必要的拷贝;对只读场景,还可能意外修改原容器(比如误用了 auto&& 但没意识到绑定的是临时对象)。
- 读取且不修改:优先用
for (const auto& x : container)—— 避免拷贝,又防误改 - 需要修改元素:用
for (auto& x : container)—— 引用可写,但确保容器本身没在循环中被增删 - 明确要副本(比如后续要异步处理):才用
for (auto x : container),但得清楚代价 - 注意
auto&&在 range-for 中通常等价于const auto&或auto&,取决于容器元素类型和 cv 限定,不推荐无脑用
std::map 和 std::unordered_map 遍历时怎么拿到 key 和 value
range-for 的迭代器解包是 std::pair<const key value></const>,不是两个独立变量。直接写 for (auto& p : my_map) 后,得通过 p.first 和 p.second 访问,否则编译失败。
- 别写
for (auto& [k, v] : my_map)—— C++17 结构化绑定虽支持,但某些老编译器(如 GCC 7.5 之前)不认,或在模板上下文中推导失败 - 如果只关心 key:用
for (const auto& kv : my_map) { use(kv.first); } - 如果 key 是字符串、int 等小类型,且确定不改 value,
for (const auto& [k, v] : my_map)可读性更好,但得确认项目 C++ 标准 ≥ 17 且工具链支持 -
std::unordered_map遍历顺序无保证,别依赖输出顺序做逻辑判断
哪些容器不能直接用范围 for 循环
不是所有“能迭代”的东西都支持 range-for。核心看有没有 begin()/end() 成员函数或 ADL 可见的自由函数,且返回类型满足输入迭代器要求。
-
std::array、std::vector、std::list、std::map等标准容器——没问题 - C 风格数组(如
int arr[5])——可以,但必须在作用域内声明,传参后退化为指针就失效 -
std::valarray、std::span(C++20)——支持 - 原始指针 + 长度(如
int* p; size_t n;)——不支持,得手写传统 for 或封装成std::span - 自定义类型:若没提供合适的
begin/end,编译报错,错误信息通常是 “no matching function for call to 'begin'”
迭代过程中修改容器导致崩溃的典型场景
范围 for 循环底层调用 begin() 和 end() 各一次,然后靠迭代器推进。一旦容器结构变化(如 push_back、erase),原有迭代器立刻失效,后续 ++ 操作未定义行为,多数情况直接 crash。
立即学习“C++免费学习笔记(深入)”;
- 绝对禁止在循环体里调用
container.push_back()、container.pop_back()、container.insert()、container.erase() - 想边遍历边删:改用传统 for +
erase()返回的迭代器,或先收集待删索引再批量删 - 想边遍历边插:先缓存新元素,循环结束后统一插入
- 多线程下更危险:即使只读,若另一线程在改容器,也需加锁或用线程安全容器(如
tbb::concurrent_vector)
事情说清了就结束。最常踩的坑不是语法写错,而是把 range-for 当成“安全黑盒”,忽略了它背后仍是普通迭代器,而迭代器失效规则一点没变。










