C++11 range for 遍历多维数组, 迭代变量添加引用 "&"的问题

来源:互联网 发布:淘宝二维码生成器 编辑:程序博客网 时间:2024/04/29 12:43

问题背景

       auto 是 C++ 11 扩展的新特性,用于自动类型推导。不过,如果你希望推导出的类型为引用(&),就要手动在变量名前加 '&';如果你还需要变量是常量的话,也要手动加入 'const' 修饰符。

       长话短说,本篇博文的目的是,阐述 使用 ranger for  遍历多维数组时, 变量名添加引用的 '&' 的问题解释它,本博文的参考文献[1] 《C++ Primer 5th》中虽有提及,但篇幅较小,所以在这里详细地理清思路。


 


引出问题

       下面,用 auto 和 range for (C++ 11 扩展的遍历特性) 来遍历 二维数组 来引出问题。(range for,样式为 for ( xxxx : yyyy),是 for 循环的一种形式,本文假定你已经知道了它的用法)


       给出代码如下所示:

#include <iostream>#include <cstddef>using namespace std;int main() {constexpr size_t rowCnt = 3, colCnt = 4;int arr[rowCnt][colCnt] = {};//initialized to all zero//method1 : successful compilation  and iterationfor(auto &row : arr)for(auto col : row)cout << col << endl;/*//method2: compile errorfor(auto row : arr)for(auto col : row)cout << col << endl;*/return 0;}




其中,方式一能成功编译,而且能正确运行;而方式二却没有通过编译,为什么 ? 下面来解释这两个方法的内部机理。


 


解释


方式一

       首先看外层循环语句,for (auto &row : arr) 这句话,定义了一个引用row,每次刷新(一个循环后,引用就过了生命周期,需要重新定义)迭代变量row的时 候,row都绑定 arr的一个长度为4的数组,等价于 int (&row) [4] = arr[i]  (i = 0, 1, 2)。

       然后 对于内层循环而言,row就是一个长度为4的一维数组的引用。

内层循环: for (auto col : row),引用 row 绑定一个 int [ ] 的一维数组,相当于就是遍历一维数组一样,没有问题。


方式二

       首先看外层循环语句,for (auto row : arr)arr 的原本的类型为 int [ ] [ ],根据参考文献[1] 的说法,row 不定义为引用的时候,arr 会自动转化为 int ** 类型,而 row 作为里面的一个元素,其类型为 int *,其值正是row对应的arr中的一个元素(一个长度为4的数组)的第一个元素的指针。

       那么在内层循环: for (auto col : row) 中,col 遍历的是 int *,而不是 int [ ]。而方式一的内层循环则是遍历的 int [ ]。

由此,我们发现,错误的根源很可能就在于----“遍历 type*出错,遍历 type[ ] 可以”


写一个例子,来验证这个问题:

int list[5] = {1,2,3,4,5};//这个语句将int []转化成了 int *,并存储到了p中int *p = list;//compile error, attempting to iterate int *for(auto e : p)cout << e << " ";//correct, attempting to iterate int []for(auto e : list)cout << e << " ";


       至此,我们的问题,就理解清楚了,加 & 相当于就是为了避免编译器自动把 [ ] 转换为 *对数组类型 [ ],编译器可以确定其边界并访问,而对于指针类型 *,编译器自己是不能确定如何去遍历的,这正是 range for --- for ( xxx : yy) 形式的不足。对于普通 for,是不存在这种问题的,因为在普通 for 的指针遍历中,我们给指针初始化了开始位置,又给它指定了越界位置,而且还能选择指针的移动方式( ++、--、+=、-= ... )。因此,本篇博文,是围绕 ranger for 来进行讨论的。 



使用decltype

       当然啦,如果你不想用 auto 来进行自动类型推导,那么可以使用 decltype。在上面的例子中,用decltype的确不容易造成混淆,看看下面的代码,是不是很简单直接啊:

for(decltype(arr[0]) &row : arr)for(decltype(arr[0][0]) col : row)cout << col << endl;
       

       当然,你要去掉 "&" 也是没有问题,因为decltype的类型推导自 arr[0] 和 arr[0][0],这已经是 int [ ] 和 int [ ] [ ] 了,编译器就不会把它们转化成 int * 和 int **了。


for(decltype(arr[0]) row : arr)for(decltype(arr[0][0]) col : row)cout << col << endl;




推广到更高维


       如果你想用 range for 的方法,来遍历更高维的数组 (dim > 2),那么你只需要:除了最内层循环之外,其他所有外层循环都加入 '&' 即可



References

[1] 《C++ Primer Fifth Edition》  Stanley B. Lippman, Josée Lajoie, Barbara E. Moo



1 0
原创粉丝点击