第 18.5 節

range-based for loop

0瀏覽次數0訪問次數--跳出率--平均停留

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

UsageExplanationWhen to use
for (auto x : c)value copy traversalElement types small (int/double, etc.)
for (const auto& x : c)read-only reference traversalMost commonly used, to avoid copying large objects.
for (auto& x : c)modifiable reference traversalNeed to modify container elements
for (int x : {1, 2, 3})Iterate through the initial values listFast 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.

RequirementRecommended Writing StyleReason
Read-only access to each elementscope formost concise
Modify the value of each elementfor (auto& x : c)directly modify the original element
Understood. Subscript.Traditional for (size_t i = 0; ...)Subscripts are part of the logic.
compare adjacent elementstraditional forTo access i and i - 1
deleting elements while traversingIterator loop / AlgorithmRange-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.


ExampleDiscusses whatNewly emerged syntaxWhy write it this wayPrecautions
Example 1The most basic range-for loop.for (int n : v)for each element n in vThis method copies each int, but since ints are small, it doesn't matter.
Example 2const auto& vs auto&const auto&auto&const auto& avoid copying; auto& modifiable elementsWhen iterating through large objects like strings, always use references.
Example 3Iterate through the initial value list and mapIterate over {...} and mapThe initial value list can be directly placed inside the forWhat is traversed out by map is std::pair
Example 4Unpacking a map with structured bindingfor (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 5Use the ~ symbol for subscripts, like this: ~text~.
For example: H~2~O renders as H₂O.std::size_t i, traditional for loopComparing 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 循环可能更合适,或者用枚举技巧。

使用建议

  • 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
  • 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
  • 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
  • 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
  • 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。

如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。

  1. Do not modify elements: use const auto& — this is the default option.
  2. Modify element: use auto& — clearly intended to modify.
  3. For basic types (int/double) that are not modified: you can use auto — the copy overhead is negligible.
  4. When you need subscripts, stick to the traditional for — range-based for isn't suitable for all scenarios.
  5. 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, auto Value 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.

音乐页