基于范围的工作对于普通数组是如何工作的?
在C ++ 11中,你可以使用基于范围的,作为其他语言的foreach
。 它甚至可以用简单的C数组工作:
int numbers[] = { 1, 2, 3, 4, 5 }; for (int& n : numbers) { n *= 2; }
它如何知道何时停止? 它只适用于已经在相同的作用域中声明的静态数组吗? 你将如何使用这个dynamic数组?
它适用于任何types为数组的expression式。 例如:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}}; for(int &n : *arraypointer) n *= 2; delete [] arraypointer;
有关更详细的解释,如果传递给右侧的expression式的types是数组types,则循环从ptr
迭代到ptr + size
( ptr
指向数组的第一个元素, size
是元素数数组)。
这与用户定义的types形成对比,如果你传递一个类对象或者(如果没有成员调用这个方法)非成员函数的话,它们通过查找begin
和end
成员来工作。 这些函数将产生开始和结束迭代器(分别指向最后一个元素和开始序列之后)。
这个问题清除了为什么这种差异存在。
我认为这个问题最重要的部分是,C ++如何知道数组的大小(当我发现这个问题时,至less我想知道它)。
C ++知道数组的大小,因为它是数组定义的一部分 – 这是variables的types。 编译器必须知道types。
由于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 ++工作草案(n3376),声明的范围相当于以下内容:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } }
所以它知道如何停止使用迭代器的常规循环。
我想你可能正在寻找像下面这样的东西来提供一种方法来使用上面的语法,它只包含一个指针和大小(dynamic数组)的数组:
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; };
然后可以使用这个类模板创build一个范围,通过这个范围你可以使用新的范围来迭代语法。 我正在使用这个来运行一个场景中的所有animation对象,这个animation对象是通过一个只返回指向一个数组和一个大小的指针的库来导入的。
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) ) { // Do something with each pAnimation instance here }
在我看来,这个语法比使用std::for_each
或者plain for
循环更清晰。
它知道何时停止,因为它知道静态数组的边界。
我不确定“dynamic数组”是什么意思,在任何情况下,如果不是迭代静态数组,非正式地,编译器会在迭代的对象类的范围内查找名称的begin
和end
,或使用参数相关查找查找begin(range)
和end(range)
,并将它们用作迭代器。
有关更多信息,请参见C ++ 11标准(或其公共草案),“6.5.4基于范围的语句”,第145页
基于范围的工作对于普通数组是如何工作的?
这是读作:“ 告诉我一个范围(与数组)? ”
我会回答假设 – 采用嵌套数组下面的例子:
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];
一些额外的信息
如果我们不想在创buildpl
时使用auto
关键字呢? 那将是什么样子?
在下面的例子中, pl
是指一个array of four integers
的array of four integers
。 在每个循环中,给定值ia[n]
:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int (&pl)[4] : ia)
而且…这就是它的工作原理,附加信息可以消除任何混淆。 这只是一个循环的简写for
它会自动计算出来,但是没有办法在没有手动的情况下检索当前的循环。