对于普通数组,基于范围的for是如何工作的?
How does the range-based for work for plain arrays?
在c++ 11中,您可以使用基于范围的for
,它充当其他语言的foreach
。它甚至可以使用普通的C数组:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
它怎么知道什么时候停止?它是否只与在for
使用的相同范围内声明的静态数组一起工作?您将如何使用这个for
与动态数组?
它适用于任何类型为数组的表达式。例如:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
n *= 2;
delete [] arraypointer;
更详细的解释是,如果传递给:
右侧的表达式类型是数组类型,则循环从ptr
迭代到ptr + size
(ptr
指向数组的第一个元素,size
是数组的元素计数)。
这与用户定义类型相反,如果传递类对象或(如果没有以这种方式调用成员)非成员函数,则通过查找begin
和end
作为成员来工作。这些函数将产生begin和end迭代器(分别直接指向序列的最后一个元素和开始元素)。
这个问题解释了为什么存在这种差异。
我认为这个问题最重要的部分是,c++如何知道数组的大小(至少当我发现这个问题时我想知道它)。
c++知道数组的大小,因为它是数组定义的一部分——它是变量的类型。编译器必须知道类型
从c++ 11开始,std::extent
可以用来获取数组的大小:
int size1{ std::extent< char[5] >::value };
std::cout << "Array size: " << size1 << std::endl;
当然,这没有多大意义,因为必须在第一行显式地提供大小,然后在第二行中获得。但你也可以使用decltype
,然后它变得更有趣:
char v[] { 'A', 'B', 'C', 'D' };
int size2{ std::extent< decltype(v) >::value };
std::cout << "Array size: " << size2 << std::endl;
根据最新的c++ Working Draft (n3376), range for语句等价如下:
{
auto && __range = range-init;
for (auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin) {
for-range-declaration = *__begin;
statement
}
}
所以它知道如何像使用迭代器的常规for
循环那样停止。
我想你可能正在寻找类似以下的东西,以提供一种方法来使用上述语法的数组,只有一个指针和大小(动态数组):
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;
};
这个类模板可以用来创建一个范围,您可以使用语法的新的范围进行迭代。我使用它来运行场景中的所有动画对象,这些对象是使用库导入的,该库只返回指向数组的指针和大小作为单独的值。
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) )
{
// Do something with each pAnimation instance here
}
在我看来,这种语法比使用std::for_each
或普通for
循环要清晰得多。
它知道什么时候停止,因为它知道静态数组的边界。
我不确定你所说的"动态数组"是什么意思,在任何情况下,如果不是在静态数组上迭代,非正式地,编译器在你迭代的对象的类范围内查找begin
和end
,或者使用依赖参数的查找查找begin(range)
和end(range)
,并将它们用作迭代器。
有关更多信息,在c++ 11标准(或其公开草案)中,"6.5.4基于范围的for
语句",pg.145
基于范围的for是如何为普通数组工作的?
读作"告诉我range -for(与数组)做什么?" "
以下是使用嵌套数组的示例:int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (auto &pl : ia)
文本版本:
ia
是一个数组的数组("嵌套数组"),包含[3]
数组,每个数组包含[4]
值。上面的例子通过ia
的主'范围' ([3]
)循环,因此循环[3]
次。每个循环产生ia
的[3]
个主值中的一个,从第一个开始,以最后一个结束——一个包含[4]
值的数组。
- 第一个循环:
pl
={1,2,3,4}
-一个数组 - 第二个循环:
pl
={5,6,7,8}
-一个数组 - 第三个循环:
pl
={9,10,11,12}
-一个数组
- 数组被解释为指向其第一个值的指针-使用数组而不进行任何迭代返回第一个值的地址
-
pl
必须是引用,因为不能复制数组 - 对于数组,当你向数组对象本身添加一个数字时,它向前移动多次并"指向"等效条目-如果
n
是所讨论的数字,那么ia[n]
与*(ia+n)
相同(我们正在解引用n
条目的地址),ia+n
与&ia[n]
相同(我们正在获取数组中该条目的地址)。
- 在每个循环中,
pl
被设置为ia[n]
的参考,n
从0开始等于当前循环计数。所以,第一轮pl
是ia[0]
,第二轮是ia[1]
,以此类推。它通过迭代获取值。 - 只要
ia+n
小于end(ia)
,循环就会继续。
…就这些了。
其实就是的简化写法:
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];
如果数组没有嵌套,那么这个过程就会变得简单一些,因为不需要引用,因为迭代的值不是数组,而是一个"正常"值:
int ib[3] = {1,2,3};
// short
for (auto pl : ib)
cout << pl;
// long
for (int n = 0; n != 3; ++n)
cout << ib[n];
一些附加信息
如果我们不想在创建pl
时使用auto
关键字该怎么办?那会是什么样子?
以"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)
…这就是它的工作原理,加上额外的信息来消除任何困惑。它只是一个自动计数的"速记"for
循环,但缺乏一种不手动检索当前循环的方法。
一些示例代码来演示堆栈上的数组与堆上的数组的区别
/**
* 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
}
- Python中的for循环与C++有何不同
- 循环仅对第一行正常工作.其他行不受 for 的影响
- for 循环中的线程无法正常工作
- For 循环使用指针遍历数组无法正常工作
- for (x:y) 循环在 C++ 中无法正常工作
- 使用 for 循环打印字符串数组无法正常工作 (C++)
- C :用sizeof()operator在for for loop中增加一个int变量,只能工作一次
- 为什么在发布模式下无法访问 for 循环,但在调试中它工作正常
- Git-Bash for Windows 'rm' 是如何工作的?
- printf保持在循环外时工作正常,但在将循环保持在for循环中时会导致一些错误
- C++范围基于for环路提供SIGABRT,而正常环路工作正常
- 无法让 for 循环工作
- 当 int 方法工作正常时,void 方法有何不同,或者为什么我不能调用 void 方法?
- c++中的文件读取:for和while嵌套循环没有按预期工作:串行
- 逗号(,)在条件部分中两个表达式之间的for循环中的工作方式
- 嵌套 for 循环 (C++) 无法正常工作
- 基于范围的循环与 for-each 循环有何不同
- 没有条件值的 FOR 循环"for (int i = 1; ; i++)"无法正常工作
- 空for(;;)怎么能工作
- 我应该如何让我的for循环工作?for循环是全新的