首页 > 编程笔记 > C++笔记 阅读:4

C++基于范围的for循环(非常详细)

许多编程语言都支持 for 循环的变体 for each 语法,即在集合的元素上重复执行一组语句。C++ 直到 C++11 才支持这一核心特性。最接近该特性的是标准库中的通用算法 std::for_each,它将函数应用于范围(range)内的所有元素。

C++11 支持 for each 语法特点的术语被称为“基于 range 的 for 循环”。C++17 标准中对其做了进一步的拓展。

在 C++11 中,基于 range 的 for 循环遵循下面的语法:
for(range_declaration : range_expression ) loop_statement
为了举例说明基于 range 的 for 循环的各种方式,我们将使用以下函数,它们返回元素序列:
std::vector<int> getRates()
{
    return std::vector<int> {1, 1, 2, 3, 5, 8, 13};
}

std::multimap<int, bool> getRates2()
{
    return std::multimap<int, bool> {
        { 1, true },
        { 1, true },
        { 2, false },
        { 3, true },
        { 5, true },
        { 8, false },
        { 13, true }
    };
}

基于 range 的 for 循环的多种使用方式:
1) 指定序列元素的特定类型:
auto rates = getRates();
for (int rate : rates)
    std::cout << rate << '\n';
for (int& rate : rates)
    rate *= 2;

2) 不指定序列元素类型,让编译器自行推导:
for (auto&& rate : getRates())
    std::cout << rate << '\n';

for (auto & rate : rates)
    rate *= 2;

for (auto const & rate : rates)
    std::cout << rate << '\n';

3) 通过在 C++17 中使用结构化绑定和分解声明:
for (auto& [rate, flag] : getRates2())
    std::cout << rate << '\n';

以上基于 range 的 for 循环的表达式基本上是语法糖,因为编译器将它转换成了其他东西。在 C++17 之前,编译器生成的代码通常像下面这样:
{
    auto && __range = range_expression;
    for (auto __begin = begin_expr, __end = end_expr;
         __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}
begin_expr 和 end_expr 在这段代码中的值取决于 range 的类型:
值得注意的是,如果一个类包含任何名为 begin 或 end 的成员(函数、数据成员或枚举器),无论其类型和可访问性如何,begin_expr 和 end_expr 都将选择它们。因此,在基于 range 的 for 循环中不能使用这种类类型。

在 C++17 中,编译器生成的代码略有不同:
{
    auto && __range = range_expression;
    auto __begin = begin_expr;
    auto __end = end_expr;
    for (; __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}
新标准删除了 begin 表达式和 end 表达式必须是同一类型的约束。end 表达式不需要是一个实际的迭代器,但它必须能够与迭代器进行比较,这样做的一个好处是可以用谓词来分隔 range。另外,end 表达式只求值一次,而不是每次迭代循环时都求值,这将会提高性能。

相关文章