35

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

 4 years ago
source link: https://www.tuicool.com/articles/aAVzUbj
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

std::vector<int> coll = { 1, 2, 3};
for( int i : coll ) {
std::cout << i << std::endl;
}

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

std::vector<int> coll = { 1, 2, 3};
for( auto i : coll ) {
std::cout << i << std::endl;
}

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

std::vector<std::string> coll = { "element1", "element2", "element3"};
for (const auto& s : coll) {
std::cout << s << std::endl;
}

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

std::map<std::string, std::string> mm;
for ( const auto& m : mm ) {
std::cout  << m.first << " < " << m.second << ">" << std::endl;
}

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

for ( for-range-declaration : expression )
statement

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

{
auto && __range = ( expression );
for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}

其中 __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放开了这个限制,将原来的等价代码修改如下:

{
auto && __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr;
for ( ; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}

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

这为类作者、库作者提供了更多的灵活性。

觉得本文不错的话,分享一下给小伙伴吧~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK