C++基于范围的for循环(非常详细)
许多编程语言都支持 for 循环的变体 for each 语法,即在集合的元素上重复执行一组语句。C++ 直到 C++11 才支持这一核心特性。最接近该特性的是标准库中的通用算法 std::for_each,它将函数应用于范围(range)内的所有元素。
C++11 支持 for each 语法特点的术语被称为“基于 range 的 for 循环”。C++17 标准中对其做了进一步的拓展。
在 C++11 中,基于 range 的 for 循环遵循下面的语法:
基于 range 的 for 循环的多种使用方式:
1) 指定序列元素的特定类型:
2) 不指定序列元素类型,让编译器自行推导:
3) 通过在 C++17 中使用结构化绑定和分解声明:
以上基于 range 的 for 循环的表达式基本上是语法糖,因为编译器将它转换成了其他东西。在 C++17 之前,编译器生成的代码通常像下面这样:
值得注意的是,如果一个类包含任何名为 begin 或 end 的成员(函数、数据成员或枚举器),无论其类型和可访问性如何,begin_expr 和 end_expr 都将选择它们。因此,在基于 range 的 for 循环中不能使用这种类类型。
在 C++17 中,编译器生成的代码略有不同:
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 的类型:
- 对于类 C 数组:它们分别是 __range 和 __range+__bound(其中 __bound 是数组中元素的数量);
- 对于具有 begin 和 end 成员(无论其类型和可访问性如何)的类类型:它们分别是 __range.begin() 和 __range.end();
- 对于其他类型,则分别是 begin(__range) 和 end(__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 表达式只求值一次,而不是每次迭代循环时都求值,这将会提高性能。