编译器支持最低版本要求:
- GCC: 4.6
- MSVC: 17.0
- Clang: 3.0
提案: N2930
遍历容器是种广泛的需求,在C++11之前,有些库提供了遍历容器内所有元素的封装方法,比如Boost中的BOOST_FOREACH,Qt中的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
则依据以下规则决定:
- 如果
__RangeT
是数组类型,则begin-expr
和end-expr
分别等于__range
和__range + __bound
,相应的__bound
是数组边界。因此如此__RangeT
是不知大小的数组,或者不完整类型(有声明没定义)的数组,那么程序就不合法。 - 如果
__RangeT
类型拥有begin()
和end()
成员函数,则begin-expr
和end-expr
分别等于__range.begin()
和__range.end()
。 - 否则,
begin-expr
和end-expr
分别等于begin(__range)
和end(__range)
,使用参数依赖查找算法进行查找,其实基本上就是std::begin()
和std::end()
。 __begin
和__end
具有相同的类型,在C++17中放宽了这个限制。
目前C++标准库中所有容器,std::string
和数组都能用这种基于范围的for循环遍历,如果想要让自己的数据结构也支持这种语法,需要满足以下要求:
- 能对此自定义数据结构类型调用
begin
和end
方法,无论是成员函数或者独立函数都可以,要能返回迭代器类型。 - 返回的迭代器类型必须支持
operator*
方法,operator!=
方法和前缀形式的operator++
方法,同样无论是成员函数或独立函数都可以。