博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

基于范围的for循环, Range-based for loop

Posted on 2017-11-28 15:12  bw_0927  阅读(1558)  评论(0)    收藏  举报

http://en.cppreference.com/w/cpp/language/range-for

 

语法:

for ( range_declaration : range_expression ) loop_statement

for (一个变量名 :  可迭代范围)  {

     //循环语句

}

变量名的类型可以是:容器元素的类型,容器元素的引用类型,auto 

 

{

auto && __range = range_expression ; 
for (auto __begin = begin_expr, __end = end_expr; 
__begin != __end; ++__begin) { 
range_declaration = *__begin; 
loop_statement 
} 

 

} 

 

 

Boost中定义了很多”范围”,很多标准库函数都使用了范围风格的实现。这一概念被C++11提了出来:

int arr[5];
vector<int> v;

for(int x: arr);
for(const int& x: arr);
for(int x: v);
for( const auto & x : arr) 
 
https://www.zhihu.com/question/65260546/answer/229573215

基于范围的for循环中原始数组可以编译通过,但是对于用指针动态创建的数组、或者数组作为参数传递时被退化成了指针却不可以,为什么?

  int a[]={1,2,3,4,5,6};
  int *p=new int[6];
  for (auto x:a) {            //同一个作用域内,数组a的类型就是数组; 但如果作为函数参数,也是不可以的
    cout<<x<<" ";
   }
   // for(auto x:p){
   //cout<<x<<" ";
   //} 编译错误

答:

简单来说, 数组和指针不是一个东西, 指针没有保存长度信息.

详细点就涉及到了C++中range based for的实现了, 实际上

for (auto x: a) { /* ... */ }

调用了 std::begin(a) 和 std::end(a) 来判断迭代的起点和终点, 也就是实际上是长这样的(假装using namespace std了)

for (auto whatever = begin(a); whatever != end(a); ++whatever) {
  auto x = *whatever;
  /* ... */
}

对于一般的重载了begin和end方法的对象(比如std::vector), 这两个全局函数会调用它们的这两个方法,

对于C风格数组, std::begin 和 std::end 是模板特化的, 简单来说就是特判了【特化为数组的引用】, 而对于指针类型, 没有重载, 所以会报错

error: ‘begin’ was not declared in this scope, error: ‘end’ was not declared in this scope

或者

error: invalid range expression of type 'int *'; no viable 'begin' function available

 

之所以没法重载指针的begin和end, 还是因为无法从指针获得长度信息.

随手打的std::begin和std::end对数组的特化

template <typename T, std::size_t N>
begin(T (&a)[N]) { return a; }        //参数:数组的引用

template <typename T, std::size_t N>
end(T (&a)[N]) { return a + N; }


使用 range-based for 对 map 的遍历方法:
  1. #include <iostream>
  2. #include <map>
  3. int main(void)
  4. {
  5. std::map<std::string, int> mm =
  6. {
  7. { "1", 1 }, { "2", 2 }, { "3", 3 }
  8. };
  9. for(auto& val : mm)
  10. {
  11. std::cout << val.first << " -> " << val.second << std::endl;
  12. }
  13. return 0;
  14. }
这里需要注意两点:
  • for 循环中 val 的类型是 std::pair。因此,对于 map 这种关联性容器而言,需要使用 val.first 或 val.second 来提取键值。
  • auto 自动推导出的类型是容器中的 value_type,而不是迭代器。