范围for循环要求容器提供begin()/end(),支持std::vector等标准容器及c数组,但不支持裸指针或unique_ptr;循环中禁止修改容器,否则迭代器失效导致未定义行为。

范围for循环的基本写法和语法结构
范围for循环(range-based for loop)在C++11引入,本质是编译器自动展开为迭代器遍历,写起来简洁,但必须满足容器支持 begin() 和 end() 成员函数或自由函数。最基础的写法是:
for (auto x : container) { /* 使用x */ }
这里 x 是值拷贝;如果不想拷贝、又不打算修改元素,应写成 const auto& x;若需修改原容器元素,则用 auto& x。别直接写 auto& 而不加 const 却只读——这会阻止编译器对临时对象(比如函数返回的右值)做绑定。
哪些容器能用,哪些会报错
能用的前提不是“是不是STL容器”,而是有没有合适的 begin()/end()。标准容器如 std::vector、std::array、std::string、std::map 都可以;C风格数组也能用(编译器推导出长度);但以下情况会失败:
-
int* p = new int[5]; for (auto x : p) {...}—— 指针没有begin/end,编译报错no matching function for call to 'begin' -
std::unique_ptr<int> arr(new int[5]); for (auto x : arr) {...}</int>—— 同样没提供迭代接口,不能直接用 - 自定义类型未定义
begin()/end()或它们返回类型不满足 InputIterator 要求
常见误用:修改容器时迭代导致未定义行为
范围for循环底层依赖迭代器,一旦在循环体内插入或删除元素(比如调用 vec.push_back() 或 vec.erase()),原有迭代器很可能失效。此时继续循环就是未定义行为——程序可能崩溃、跳过元素,或看似正常但结果不可靠。
正确做法是:
- 需要边遍历边删:改用传统 for + 迭代器,配合
erase()返回的下一个有效迭代器 - 需要边遍历边增:先收集要添加的元素,循环结束后再批量插入
- 想跳过某些元素:用
continue,但不要动容器本身
例如:for (auto& x : vec) { if (x 是危险的,即使当前编译通过、运行没崩,也不能保证下次还安全。
性能和类型推导要注意的细节
auto 推导类型时不会自动退化为引用,所以 for (auto x : vec) 对 std::vector<:string></:string> 会触发多次字符串拷贝。大对象务必显式加引用:
- 只读 →
const auto& x - 需修改 →
auto& x - 确定是小 POD 类型(如
int、double)且逻辑上不关心拷贝开销,才用auto x
另外,std::initializer_list 是特例:它支持范围for,但它是只读、不可修改的;而且它的 begin()/end() 返回的是 const 指针,所以即使写 auto& x 也会编译失败,必须用 const auto& x。
真正容易被忽略的是:范围for无法获取当前索引,也没有内置的“是否是最后一个元素”判断。需要索引就得自己维护计数器,或者换回传统 for 循环——别硬套语法糖去凑功能。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











