在二维数组上使用for_each和范围基础for循环

6
我试图使用for_each和基于范围的for循环打印一个二维数组。
我的程序如下:
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
  int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
  //for_each (begin(a), end(a), [] (int x) { cout<<x<<" ";}); this code throws error

  for_each (begin(a[0]), end(a[2]), [] (int x) { cout<<x<<" ";});  //this code works well, why ?

  cout<<endl;

  for  (auto &row: a)  // without & for row, error is thrown
  {
     for (auto x:row)  // no & needed for x, why ?
     {
        cout<<x<<" ";
     }
  }
  return 0;
}

我的第一个for_each为什么会报错,为什么在row上需要使用&符号?它的类型是什么?row是一个指针吗?

2个回答

4
for_each (begin(a), end(a), [] (int x) { cout<<x<<" ";});

begin(a) 产生一个 int(*)[3](大小为 [3] 的数组的指针),对它进行解引用会产生一个 int(&)[3],而你的 lambda 表达式需要一个 int 参数。

for_each (begin(a[0]), end(a[2]), [] (int x) { cout<<x<<" ";});
begin(a[0])产生一个指向a的第一行中第一个元素的int*,而end(a[2])产生一个指向a的最后一行中最后一个元素之后的位置的int*,所以一切正常。
现在考虑基于范围的for部分。
如果你从for(auto& row : a)这一行中去掉&,那么错误实际上会出现在下一行的for(auto x : row)。这是因为基于范围的for的规定方式。与您的用例相关的子句如下:

如果__range是一个数组,则begin_expr__range,而end_expr(__range + __bound),其中__bound是数组中的元素数(如果数组大小未知或为不完整类型,则程序无效)

以下我将提到链接页面Explanation部分中提到的标识符。
考虑for(auto& row : a)这种情况: __range被推断为int(&)[3][3](大小为[3][3]的数组引用)。然后__begin被推断为int(*)[3](大小为[3]的数组指针),因为__range的类型衰减为二维数组的第一行的指针。你有一个range_expression,即auto& row,所以row被推断为int(&)[3](大小为[3]的数组引用)。
接下来,内部基于范围的for重复了相同的过程。在这种情况下,__rangeint(&)[3],并且我引用的数组子句适用;剩余的类型推导过程类似于我上面描述的过程。
__range = int(&)[3]
__begin = int*
x       = int

现在考虑使用 for (auto row : a) 的情况:

__range__begin__end 都被推断为相同的类型。该情况的关键区别在于 range_expressionauto row,这会导致 __begin 推断为 int(*)[3] 类型的衰退。这意味着 row 被推断为 int *,并且描述 begin_expr/end_expr 确定的 3 个子句均不处理原始指针。这导致嵌套的 for 循环中出现编译错误。


3

a是一个二维数组 - 如果您愿意,可以是int[][]

这意味着当您遍历a时,您只遍历第一个数组维度 - 从a[0]a[2]a[0]仍然是一个数组,这就解释了为什么您的第一个for_each可能会产生错误 - 您提供的lambda表达式期望一个int,但实际上会传递一个int*

第一个基于范围的for循环需要引用(&符号),原因相同。如果没有&,编译器将尝试按值复制第一维中的项目,但这些本身就是数组,您无法通过值复制数组。

第二个基于范围的for循环不需要引用,因为它正在遍历第二个数组维度,其中包含简单的整数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接