C++ 普通数组的基于范围的工作如何?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7939399/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:45:05  来源:igfitidea点击:

How does the range-based for work for plain arrays?

c++arraysforeachc++11

提问by Paul Manta

In C++11 you can use a range-based for, which acts as the foreachof other languages. It works even with plain C arrays:

在 C++11 中,您可以使用基于范围的for,它充当foreach其他语言的 。它甚至适用于普通的 C 数组:

int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
    n *= 2;
}

How does it know when to stop? Does it only work with static arrays that have been declared in the same scope the foris used in? How would you use this forwith dynamic arrays?

它怎么知道什么时候停止?它是否仅适用于在使用的同一范围内声明的静态数组for?您将如何将其for与动态数组一起使用?

采纳答案by Johannes Schaub - litb

It works for any expression whose type is an array. For example:

它适用于任何类型为数组的表达式。例如:

int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
  n *= 2;
delete [] arraypointer;

For a more detailed explanation, if the type of the expression passed to the right of :is an array type, then the loop iterates from ptrto ptr + size(ptrpointing to the first element of the array, sizebeing the element count of the array).

更详细的解释,如果传递给右侧的表达式的类型:是数组类型,则循环从ptrto ptr + sizeptr指向数组的第一个元素,即数组size的元素计数)进行迭代。

This is in contrast to user defined types, which work by looking up beginand endas members if you pass a class object or (if there is no members called that way) non-member functions. Those functions will yield the begin and end iterators (pointing to directly after the last element and the begin of the sequence respectively).

这与用户定义的类型形成对比,如果您传递类对象或(如果没有以这种方式调用的成员)非成员函数,则用户定义的类型通过查找beginend作为成员来工作。这些函数将产生开始和结束迭代器(分别指向最后一个元素和序列的开始之后)。

This questionclears up why that difference exists.

这个问题澄清了为什么存在这种差异。

回答by psur

I think that the most important part of this question is, how C++ knows what the size of an array is (at least I wanted to know it when I found this question).

我认为这个问题最重要的部分是,C++ 如何知道数组的大小(至少我在发现这个问题时想知道它)。

C++ knows the size of an array, because it's a part of the array's definition - it's the type of the variable. A compiler has to know the type.

C++ 知道数组的大小,因为它是数组定义的一部分——它是变量的类型。编译器必须知道类型。

Since C++11 std::extentcan be used to obtain the size of an array:

由于 C++11std::extent可用于获取数组的大小:

int size1{ std::extent< char[5] >::value };
std::cout << "Array size: " << size1 << std::endl;

Of course, this doesn't make much sense, because you have to explicitly provide the size in the first line, which you then obtain in the second line. But you can also use decltypeand then it gets more interesting:

当然,这没有多大意义,因为您必须在第一行中明确提供大小,然后在第二行中获得。但是你也可以使用decltype然后它变得更有趣:

char v[] { 'A', 'B', 'C', 'D' };
int size2{ std::extent< decltype(v) >::value };
std::cout << "Array size: " << size2 << std::endl;

回答by Grant

According to the latest C++ Working Draft (n3376) the ranged for statement is equivalent to the following:

根据最新的 C++ 工作草案 (n3376),ranged for 语句等效于以下内容:

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

So it knows how to stop the same way a regular forloop using iterators does.

所以它知道如何像for使用迭代器的常规循环一样停止。

I think you may be looking for something like the following to provide a way to use the above syntax with arrays which consist of only a pointer and size (dynamic arrays):

我认为您可能正在寻找类似以下内容的方法,以提供一种将上述语法与仅由指针和大小(动态数组)组成的数组一起使用的方法:

template <typename T>
class Range
{
public:
    Range(T* collection, size_t size) :
        mCollection(collection), mSize(size)
    {
    }

    T* begin() { return &mCollection[0]; }
    T* end () { return &mCollection[mSize]; }

private:
    T* mCollection;
    size_t mSize;
};

This class template can then be used to create a range, over which you can iterate using the new ranged forsyntax. I am using this to run through all animation objects in a scene which is imported using a library that only returns a pointer to an array and a size as separate values.

然后可以使用此类模板创建一个范围,您可以使用新的ranged for语法在该范围内进行迭代。我使用它来运行场景中的所有动画对象,该场景是使用库导入的,该库仅返回指向数组的指针和大小作为单独的值。

for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) )
{
    // Do something with each pAnimation instance here
}

This syntax is, in my opinion, much clearer than what you would get using std::for_eachor a plain forloop.

在我看来,这种语法比您使用的语法std::for_each或普通for循环要清晰得多。

回答by Super Cat

How does the range-based for work for plain arrays?

普通数组的基于范围的工作如何?

Is that to read as, "Tell me what a ranged-for does (with arrays)?"

是不是应该读作,“告诉我一个 ranged-for 是做什么的(使用数组)?

I'll answer assuming that - Take the following example using nested arrays:

我将回答假设 - 以使用嵌套数组的以下示例为例:

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};

for (auto &pl : ia)

Text version:

文字版:

iais an array of arrays ("nested array"), containing [3]arrays, with each containing [4]values. The above example loops through iaby it's primary 'range' ([3]), and therefore loops [3]times. Each loop produces one of ia's [3]primary values starting from the first and ending with the last - An array containing [4]values.

ia是一个数组数组(“嵌套数组”),包含[3]数组,每个数组包含[4]值。上面的示例通过ia它的主要“范围”([3])循环,因此循环[3]次数。每个循环产生一个从第一个开始到最后一个结束ia[3]主要值 - 一个包含[4]值的数组。

  • First loop: plequals {1,2,3,4}- An array
  • Second loop: plequals {5,6,7,8}- An array
  • Third loop: plequals {9,10,11,12}- An array
  • 第一个循环:plequals {1,2,3,4}- 一个数组
  • 第二个循环:plequals {5,6,7,8}- 一个数组
  • 第三个循环:plequals {9,10,11,12}- 一个数组

Before we explain the process, here are some friendly reminders about arrays:

在我们解释这个过程之前,这里有一些关于数组的友好提示:

  • Arrays are interpreted as pointers to their first value - Using an array without any iteration returns the address of the first value
  • plmustbe a reference because we cannot copy arrays
  • With arrays, when you add a number to the array object itself, it advances forward that many times and 'points' to the equivalent entry - If nis the number in question, then ia[n]is the same as *(ia+n)(We're dereferencing the address that's nentries forward), and ia+nis the same as &ia[n](We're getting the address of the that entry in the array).
  • 数组被解释为指向它们的第一个值的指针 - 使用没有任何迭代的数组返回第一个值的地址
  • pl必须是引用,因为我们不能复制数组
  • 对于数组,当您向数组对象本身添加一个数字时,它会向前推进多次并“指向”到等效的条目 - 如果n是有问题的数字,则ia[n]*(ia+n)(我们正在取消引用n条目的地址相同)转发),并且ia+n&ia[n](我们正在获取数组中该条目的地址)相同。

Here's what's going on:

这是发生了什么:

  • On each loop, plis set as a referenceto ia[n], with nequaling the current loop count starting from 0. So, plis ia[0]on the first round, on the second it's ia[1], and so on. It retrieves the value via iteration.
  • The loop goes on so long as ia+nis less than end(ia).
  • 在每个循环中,pl被设置为基准ia[n],用n等于从0开始那么当前循环计数,plia[0]在第一轮,第二它的ia[1],等等。它通过迭代检索值。
  • 只要ia+n小于,循环就会继续end(ia)

...And that's about it.

......就是这样。

It's really just a simplified way to write this:

这实际上只是一种简化的编写方式

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int n = 0; n != 3; ++n)
  auto &pl = ia[n];


If your array isn'tnested, then this process becomes a bit simpler in that a reference is notneeded, because the iterated value isn't an array but rather a 'normal' value:

如果阵列没有嵌套,那么这个过程变成它是引用简单一点不是必要的,因为迭代值是不是一个数组,而是一个“正常”的价值:

 int ib[3] = {1,2,3};

 // short
 for (auto pl : ib)
   cout << pl;

 // long
 for (int n = 0; n != 3; ++n)
   cout << ib[n];


Some additional information

一些附加信息

What if we didn't want to use the autokeyword when creating pl? What would that look like?

如果我们不想auto在创建时使用关键字pl怎么办?那会是什么样子?

In the following example, plrefers to an array of four integers. On each loop plis given the value ia[n]:

在以下示例中,pl指的是array of four integers. 在每个循环pl上给出值ia[n]

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int (&pl)[4] : ia)


And... That's how it works, with additional information to brush away any confusion. It's just a 'shorthand' forloop that automatically counts for you, but lacks a way to retrieve the current loop without doing it manually.

并且......这就是它的工作原理,附加信息可以消除任何混淆。它只是一个for自动为您计数的“速记”循环,但缺乏一种无需手动执行即可检索当前循环的方法。

回答by chill

It knows when to stop because it knows the bounds of static arrays.

它知道何时停止,因为它知道静态数组的边界。

I'm not sure what do you mean by "dynamic arrays", in any case, if not iterating over static arrays, informally, the compiler looks up the names beginand endin the scope of the class of the object you iterate over, or looks up for begin(range)and end(range)using argument-dependent lookup and uses them as iterators.

我不确定“动态数组”是什么意思,在任何情况下,如果不迭代静态数组,非正式地,编译器会在您迭代的对象的类的范围内查找名称beginend范围,或查找up forbegin(range)end(range)使用参数相关的查找并将它们用作迭代器。

For more information, in the C++11 standard (or public draft thereof), "6.5.4 The range-based forstatement", pg.145

有关更多信息,请参阅 C++11 标准(或其公开草案),“6.5.4 The range-based forstatement”,第 145 页

回答by Yip Cubed

Some sample code to demonstrate the difference between arrays on Stack vs arrays on Heap

一些示例代码来演示堆栈上的数组与堆上的数组之间的区别


/**
 * Question: Can we use range based for built-in arrays
 * Answer: Maybe
 * 1) Yes, when array is on the Stack
 * 2) No, when array is the Heap
 * 3) Yes, When the array is on the Stack,
 *    but the array elements are on the HEAP
 */
void testStackHeapArrays() {
  int Size = 5;
  Square StackSquares[Size];  // 5 Square's on Stack
  int StackInts[Size];        // 5 int's on Stack
  // auto is Square, passed as constant reference
  for (const auto &Sq : StackSquares)
    cout << "StackSquare has length " << Sq.getLength() << endl;
  // auto is int, passed as constant reference
  // the int values are whatever is in memory!!!
  for (const auto &I : StackInts)
    cout << "StackInts value is " << I << endl;

  // Better version would be: auto HeapSquares = new Square[Size];
  Square *HeapSquares = new Square[Size];   // 5 Square's on Heap
  int *HeapInts = new int[Size];            // 5 int's on Heap

  // does not compile,
  // *HeapSquares is a pointer to the start of a memory location,
  // compiler cannot know how many Square's it has
  // for (auto &Sq : HeapSquares)
  //    cout << "HeapSquare has length " << Sq.getLength() << endl;

  // does not compile, same reason as above
  // for (const auto &I : HeapInts)
  //  cout << "HeapInts value is " << I << endl;

  // Create 3 Square objects on the Heap
  // Create an array of size-3 on the Stack with Square pointers
  // size of array is known to compiler
  Square *HeapSquares2[]{new Square(23), new Square(57), new Square(99)};
  // auto is Square*, passed as constant reference
  for (const auto &Sq : HeapSquares2)
    cout << "HeapSquare2 has length " << Sq->getLength() << endl;

  // Create 3 int objects on the Heap
  // Create an array of size-3 on the Stack with int pointers
  // size of array is known to compiler
  int *HeapInts2[]{new int(23), new int(57), new int(99)};
  // auto is int*, passed as constant reference
  for (const auto &I : HeapInts2)
    cout << "HeapInts2 has value " << *I << endl;

  delete[] HeapSquares;
  delete[] HeapInts;
  for (const auto &Sq : HeapSquares2) delete Sq;
  for (const auto &I : HeapInts2) delete I;
  // cannot delete HeapSquares2 or HeapInts2 since those arrays are on Stack
}