Modern C++特性:基于范围的for循环

遍历容器是种广泛的需求,在C++11之前,有些库提供了遍历容器内所有元素的封装方法,比如Boost中的BOOST_FOREACH,Qt中的foreach关键字等等,甚至C++自己也提供了一个std::for_each算法。

C++11基于范围的for循环

  1. std::vector<int> coll = { 1, 2, 3};

  2. for( int i : coll ) {

  3. std::cout << i << std::endl;

  4. }

以上代码以值拷贝方式访问到容器coll中的每个元素,这里可以使用 auto来自动推导容器内元素的类型:

  1. std::vector<int> coll = { 1, 2, 3};

  2. for( auto i : coll ) {

  3. std::cout << i << std::endl;

  4. }

如果需要修改容器内元素的内容,则需要声明引用类型 int& i或 auto& i。所以当容器内元素类型是复杂数据类型时,为运行效率考虑计,一般推荐引用或常量引用方式访问:

  1. std::vector<std::string> coll = { "element1", "element2", "element3"};

  2. for (const auto& s : coll) {

  3. std::cout << s << std::endl;

  4. }

std::map是按 std::pair迭代的,所以要这样遍历 std::map

  1. std::map<std::string, std::string> mm;

  2. for ( const auto& m : mm ) {

  3. std::cout << m.first << " < " << m.second << ">" << std::endl;

  4. }

一般而言,如下一组基于范围的for循环:

  1. for ( for-range-declaration : expression )

  2. statement

等价于如下一组老式的for循环:

  1. {

  2. auto && __range = ( expression );

  3. for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) {

  4. for-range-declaration = *__begin;

  5. statement

  6. }

  7. }

其中 __range, __begin和 __end仅用于说明, __RangeT是 expression的类型, begin-expr和 end-expr则依据以下规则决定:

  1. 如果 __RangeT是数组类型,则 begin-expr和 end-expr分别等于 __range和 __range +__bound,相应的 __bound是数组边界。因此如此 __RangeT是不知大小的数组,或者不完整类型(有声明没定义)的数组,那么程序就不合法。

  2. 如果 __RangeT类型拥有 begin()和 end()成员函数,则 begin-expr和 end-expr分别等于 __range.begin()和 __range.end()

  3. 否则, begin-expr和 end-expr分别等于 begin(__range)和 end(__range),使用参数依赖查找算法进行查找,其实基本上就是 std::begin()和 std::end()

  4. __begin和 __end具有相同的类型,在C++17中放宽了这个限制。

目前C++标准库中所有容器, std::string和数组都能用这种基于范围的for循环遍历,如果想要让自己的数据结构也支持这种语法,需要满足以下要求:

  1. 能对此自定义数据结构类型调用 begin和 end方法,无论是成员函数或者独立函数都可以,要能返回迭代器类型。

  2. 返回的迭代器类型必须支持 operator*方法, operator!=方法和前缀形式的 operator++方法,同样无论是成员函数或独立函数都可以。

C++17的改进

C++11引入的基于范围的for循环要求 begin和 end(起始值和结尾值)具有相同的类型,这对于大多数情况来说并没有什么问题,比如在遍历STL容器时,总是能返回相同类型的 begin和 end

但是有人觉得这个规范过于受限,于是C++17放开了这个限制,将原来的等价代码修改如下:

  1. {

  2. auto && __range = for-range-initializer;

  3. auto __begin = begin-expr;

  4. auto __end = end-expr;

  5. for ( ; __begin != __end; ++__begin ) {

  6. for-range-declaration = *__begin;

  7. statement

  8. }

  9. }

与C++11中的相比,唯一的不同就是 __begin和 __end可以具有不同类型了,只要它们两个支持通过 operator!=比较即可。

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值