范围for循环基本写法为for (auto& x : container),要求容器支持begin()/end(),原生数组等不适用;性能通常与传统for一致,但end()非o(1)或迭代中修改容器会导致未定义行为。

范围for循环的基本写法长什么样
范围for循环在C++11之后才正式可用,核心是用 for (auto& x : container) 这种结构替代传统迭代器写法。它不是语法糖,底层会调用容器的 begin() 和 end(),所以要求容器必须支持这两个成员函数或自由函数。
常见误写:for (auto x : vec) 默认按值拷贝,对 std::string、std::vector 等类型可能触发不必要复制;改用 auto& 或 const auto& 更安全。
- 只读遍历 → 用
const auto& x - 需要修改元素 → 用
auto& x - 确定是小POD类型(如
int、double)且不关心性能 →auto x可接受
哪些容器不能直接用范围for
原生数组、C风格字符串、动态分配的指针数组(如 int* p = new int[10])都不满足“有 begin()/end()”的要求,编译直接报错:error: no matching function for call to 'begin(int*&)' 。
解决办法不是硬套,而是补足接口或换方式:
立即学习“C++免费学习笔记(深入)”;
- 原生数组 → 用
std::array或std::span(C++20),或手动传入指针范围:for (auto& x : std::span{p, size}) - C字符串 → 先转
std::string_view(C++17):for (char c : std::string_view{"hello"}) - 自定义类 → 必须提供
begin()和end()成员函数,返回可解引用、可递增、可比较的迭代器类型
和传统for/迭代器比,性能差在哪
绝大多数情况下,范围for和手写 for (auto it = c.begin(); it != c.end(); ++it) 生成的汇编完全一致,编译器能完美优化。但有两个真实坑点:
-
c.end()在每次循环中被重复调用 —— 如果容器的end()不是 O(1)(比如某些自定义链表实现),就会拖慢速度 - 隐式转换干扰:若容器重载了
begin()但返回类型不标准(比如返回void*),编译器可能选错重载,报错信息晦涩,例如error: invalid initialization of reference of type 'int&' from expression of type 'void'
实操建议:对性能敏感路径,先看编译后汇编;不确定时,用 auto it_end = c.end(); for (auto it = c.begin(); it != it_end; ++it) 更可控。
迭代过程中修改容器会怎样
所有标准容器(std::vector、std::map、std::list等)在范围for中执行 push_back、erase、insert 都属于未定义行为(UB)。不是“大概率崩溃”,而是编译器可以任意处理,包括静默出错。
原因很简单:范围for的 begin()/end() 结果在循环开始时就固定了,后续容器变动(尤其是内存重分配)会让这些迭代器失效。
- 要边遍历边删 → 改用
erase-remove惯用法,或手写反向迭代器遍历 - 要边遍历边增 → 提前预留容量(
vec.reserve()),或把新增元素暂存到另一个容器里,循环结束后再合并 - 调试时加
-D_GLIBCXX_DEBUG(GCC)可让大部分越界/失效访问立刻报错
最常被忽略的是:即使你没显式调用 erase,只要容器内部发生 realloc(比如 vector 超过 capacity),当前循环中的 auto& x 就已悬空。










