range-based for loop
What problem does this section solve?
Traditional microcontroller loop iteration over containers requires writing three components (initialization, condition, increment), which is error-prone: out-of-bounds indices, incorrect boundary conditions, type mismatches, and so on.
The range-based for loop allows you to write code that operates on each element in a container without worrying about subscripts or iterators, making the code more concise and safe.
What is this feature?
The range-based for loop is a syntactic sugar introduced in C++11 that allows you to directly iterate over each element in a container (or any type with begin()/end()), with compilers automatically generating the corresponding iterator code.
C++ standard version
C++11 (basics), C++20 added initialization statement support.
Required header files
No extra header files are needed. Range-based for is a language feature. However, the container being iterated over must include its corresponding header file (e.g., <vector>).
Basic Syntax
for (元素类型 变量名 : 容器)
{
// 使用变量名
}
// 推荐:不修改元素用 const auto&
for (const auto& 变量名 : 容器)
{
// 只读访问
}
// 要修改元素用 auto&
for (auto& 变量名 : 容器)
{
// 可以修改元素
}
Common Usage
| Usage | Explanation | When to use |
|---|---|---|
for (auto x : c) | value copy traversal | Element types small (int/double, etc.) |
for (const auto& x : c) | read-only reference traversal | Most commonly used, to avoid copying large objects. |
for (auto& x : c) | modifiable reference traversal | Need to modify container elements |
for (int x : {1, 2, 3}) | Iterate through the initial values list | Fast traversal of a known small set |
When you don't use range-based for
Range-based for is suitable for "accessing elements one by one." If your logic relies on indices, adjacent elements, reverse traversal, skipping iterations, or modifying container elements during traversal, traditional for loops or iterator loops are clearer instead.
| Requirement | Recommended Writing Style | Reason |
|---|---|---|
| Read-only access to each element | scope for | most concise |
| Modify the value of each element | for (auto& x : c) | directly modify the original element |
| Understood. Subscript. | Traditional for (size_t i = 0; ...) | Subscripts are part of the logic. |
| compare adjacent elements | traditional for | To access i and i - 1 |
| deleting elements while traversing | Iterator loop / Algorithm | Range-for loops are prone to triggering iterator invalidation. |
Example code
Example 1: Traversing a vector using range for
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {10, 20, 30, 40, 50};
// 范围 for 遍历
std::cout << "elements: ";
for (int n : v)
{
std::cout << n << " ";
}
std::cout << "\n";
return 0;
}
Results:
elements: 10 20 30 40 50
Example 2: Based on Example 1, the difference between const auto& and auto&
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
// const auto&:只读遍历,不拷贝(推荐)
std::cout << "const auto&: ";
for (const auto& s : names)
{
std::cout << s << " ";
// s = "X"; // ❌ 编译错误!const 引用不能修改
}
std::cout << "\n";
// auto&:可修改遍历
for (auto& s : names)
{
s = s + "!"; // 修改原容器中的元素
}
std::cout << "after modify: ";
for (const auto& s : names)
{
std::cout << s << " ";
}
std::cout << "\n";
return 0;
}
Results:
const auto&: Alice Bob Charlie
after modify: Alice! Bob! Charlie!
Example 3: Based on Example 2, iterating through the initial value list and map
#include <iostream>
#include <map>
#include <string>
int main()
{
// 直接遍历初始值列表
std::cout << "init list: ";
for (int n : {3, 1, 4, 1, 5, 9})
{
std::cout << n << " ";
}
std::cout << "\n";
// 遍历 map
std::map<std::string, int> scores = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78}
};
std::cout << "scores:\n";
for (const auto& pair : scores)
{
std::cout << " " << pair.first << ": " << pair.second << "\n";
}
return 0;
}
Results:
init list: 3 1 4 1 5 9
scores:
Alice: 85
Bob: 92
Charlie: 78
Example 4: Based on Example 3, using C++17 structured bindings to traverse a map
#include <iostream>
#include <map>
#include <string>
int main()
{
std::map<std::string, int> scores = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78}
};
// C++17 结构化绑定:直接解包 key 和 value
std::cout << "scores (structured binding):\n";
for (const auto& [name, score] : scores)
{
std::cout << " " << name << ": " << score << "\n";
}
return 0;
}
Results:
scores (structured binding):
Alice: 85
Bob: 92
Charlie: 78
Example 5: Based on Example 4, when subscripts are required, do not force the use of range-for loops.
#include <cstddef>
#include <iostream>
#include <string>
#include <vector>
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
// vector 是动态数组,元素数量可以在运行时变化。
std::vector<int> temperatures = {22, 23, 21, 25, 24};
std::cout << "all temperatures: ";
for (int t : temperatures)
{
std::cout << t << " ";
}
std::cout << "\n";
std::cout << "changes between adjacent days:\n";
for (std::size_t i = 1; i < temperatures.size(); ++i)
{
int change = temperatures[i] - temperatures[i - 1];
std::cout << " day " << i << " -> day " << (i + 1)
<< ": " << change << "\n";
}
return 0;
}
Results:
all temperatures: 22 23 21 25 24
changes between adjacent days:
day 1 -> day 2: 1
day 2 -> day 3: -2
day 3 -> day 4: 4
day 4 -> day 5: -1
runtime results
See the "running results" for each example above.
Key syntax explanation in the example
|Here is the translation of the provided Simplified Chinese Markdown fragment into natural American English, following all specified rules.
| Example | Discusses what | Newly emerged syntax | Why write it this way | Precautions |
|---|---|---|---|---|
| Example 1 | The most basic range-for loop. | for (int n : v) | for each element n in v | This method copies each int, but since ints are small, it doesn't matter. |
| Example 2 | const auto& vs auto& | const auto&、auto& | const auto& avoid copying; auto& modifiable elements | When iterating through large objects like strings, always use references. |
| Example 3 | Iterate through the initial value list and map | Iterate over {...} and map | The initial value list can be directly placed inside the for | What is traversed out by map is std::pair |
| Example 4 | Unpacking a map with structured binding | for (const auto& [k, v] : m) | C++17 syntax allows for clearer naming of keys and values separately. | If C++17 cannot be used, you can opt for the approach in Example 3. Could you please provide more details about Example 3 so I can give a more specific suggestion? |
| Example 5 | Use the ~ symbol for subscripts, like this: ~text~. | |||
For example: H~2~O renders as H₂O. | std::size_t i, traditional for loop | Comparing adjacent elements cannot do without subscripts. | Don't sacrifice clarity for the sake of using new syntax. |
Common Errors
Error 1: Adding or removing elements during iteration
for (auto x : v)
{
v.push_back(x * 2); // ❌ 可能导致迭代器失效、无限循环!
}
Correct approach: Collect new containers first and perform unified operations after the loop ends, or use a traditional iterator for looping.
Error 2: Using auto without reference, changes are ineffective
for (auto s : names)
{
s = "X"; // 修改的是拷贝!
}
// names 没有变化!
Correct approach: To modify container elements, use for (auto& s : names).
Error 3: Accessing another container by index while iterating
for (int n : v1)
{
v2[i++] = n; // i 从哪来?
}
正确做法:需要下标时,传统 for 循环可能更合适,或者用枚举技巧。
使用建议
- 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
- 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
- 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
- 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
- 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。
如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。
- Do not modify elements: use
const auto&— this is the default option. - Modify element: use
auto&— clearly intended to modify. - For basic types (int/double) that are not modified: you can use
auto— the copy overhead is negligible. - When you need subscripts, stick to the traditional for — range-based for isn't suitable for all scenarios.
- For map traversal, preferably use
[key, value]structured bindings (from C++17 onwards).
Summary
- The syntax for range-based for loop to traverse a container is
for (const auto& x : container). const auto&Read-only references (the default choice),auto&Modifiable references,autoValue copies.- Applicable to any type that has
begin()/end()(STL containers, initializer lists, etc.). - During traversal, you cannot add or remove elements from the container, as it will cause iterator invalidation.
Engineering expansion
In ROS2, range-based for loops are frequently used for scenarios like iterating over sensor data lists or node lists. Similarly, in Boost.Asio, range-based for loops are often employed to traverse connection lists. By mastering the idiom const auto&, you can write code that matches the style of excellent open-source projects.