我正在查看http://www.cplusplus.com/doc/tutorial/arrays/中的以下示例,但我无法弄清楚第二个for循环是如何工作的。 for循环如何知道数组何时结束。如果它能弄明白为什么第一个循环不使用类似的方法?我的印象是无法确定阵列的长度。我不确定如何调和这些观念。谢谢!
编辑:感谢所有出色的答案!
#include <iostream>
using namespace std;
int main()
{
int myarray[3] = {10,20,30};
for (int i=0; i<3; ++i)
++myarray[i];
for (int elem : myarray)
cout << elem << '\n';
}
答案 0 :(得分:5)
这有效的原因是for
循环有效 1 使用std::begin
和std::end
。反过来,这些工作,因为它们专门为内置数组提供重载,如下所示:
template <class T, size_t N>
T *begin(T (&array)[N]) {
return array;
}
template <class T, size_t N>
T *end(T (&array)[N]) {
return array+N;
}
尽管在原始(1998)C ++标准发布之前(显然)尚未实现,但该技术并不需要除C ++ 98中提供的任何语言功能之外的任何语言功能。 C ++ 11编写了该技术并将其付诸实施。
因为在这种情况下,参数被指定为对数组的引用,所以只有当参数确实是一个数组时,类型推导才会成功。对于std::begin
,还有一些版本支持其他参数类型,并使用(例如)集合的begin()
和end()
成员(如果该类型匹配)。
<子>
1.&#34;有效地&#34;在这种情况下意味着在某些情况下,基于范围的for循环使用begin
和end
,而其他情况则不是。{1}}和begin
。如果您进入技术领域,它们不会用于数组,但会直接进行类似的计算。同样,对于具有end
和begin(range)
成员的容器类型,可以直接使用这些成员。如果这两个都不属实,则使用end(range)
和std::begin
,可以使用std::end
/ begin(x)
或end(x)
/ {{1} }由参数依赖查找找到的对。
子>
答案 1 :(得分:4)
由于数组定义,编译器知道数组的元素数。因此它使用表达式myarray
和myarray + 3
来遍历数组。
事实上,循环看起来如下
for ( auto first = myarray, last = myarray + 3; first != last; ++first )
{
auto elem = *first;
cout << elem << '\n';
}
考虑到基于范围的for语句既不使用std :: begin()也不使用数组的std :: end(),就像其他人在这里写的那样。:)
根据C ++标准
- 如果 _RangeT是数组类型,则begin-expr和end-expr为 分别为 __ range 和 __ range + __bound ,其中__bound是数组绑定的。如果_RangeT是一个未知大小的数组或一个数组 不完整的类型,程序是不正确的;
答案 2 :(得分:4)
编译器知道数组的实际类型,并使用基于标准迭代器的循环array
(相当于array + 0
)和array + length
(相当于*(&array + 1)
)作为范围的各自结束。
对于非数组,它首先在给定范围内查找begin
和end
,然后最终使用ADL查找自由函数。
std::begin
和std::end
将平均使用ADL查找免费函数,因此,如果您在代码中使用using std::begin;
和using std::end;
,则不合格的名称 - 查找将模仿ranged-for-loop的规则。
来自C ++ 1y的正确部分:
6.5.4基于范围的语句[stmt.ranged]
1对于表单
的基于范围的for
语句for ( for-range-declaration : expression ) statement
让
range-init
等同于括号 90expression
所包围的( expression )
以及基于范围的for语句for ( for-range-declaration : braced-init-list ) statement
让
range-init
等同于braced-init-list
。在每种情况下,基于范围的for
语句等同于{ auto && __range = range-init; for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) { for-range-declaration = *__begin; statement } }
其中
__range
,__begin
和__end
是仅为展示定义的变量,_RangeT
是表达式的类型,begin-expr
和{ {1}}确定如下:
- 如果
end-expr
是数组类型,则_RangeT
和begin-expr
分别为end-expr
和__range
,其中__range + __bound
是数组绑定。如果__bound
是未知大小的数组或不完整类型的数组,则该程序格式不正确;- 如果
_RangeT
是类类型,则在类_RangeT
的范围内查找unqualified-idsbegin
和end
,就像通过类成员访问查找一样( 3.4.5),如果其中任何一个(或两个)发现至少一个声明,_RangeT
和beginexpr
分别为end-expr
和__range.begin()
;- 否则,
__range.end()
和begin-expr
分别为end-expr
和begin(__range)
,其中end(__range)
和begin
在关联的命名空间中查找(3.4.2)。 [注意:不执行普通的非限定查找(3.4.1)。 - 尾注]2在 for-range-declaration 的 decl-specifier-seq 中,每个 decl-specifier 应为 >类型说明符或 constexpr 。
答案 3 :(得分:3)
你有一种误解,认为远程需要提前知道迭代次数(&#34;长度&#34;)。它没有。
确实需要终止条件,其格式为it != __end
__end = x.end()
或__end = end(x)
。
当然,数组不能改变它们的大小,所以在这种情况下检测结束并知道长度是等效的(长度可以从开始,结束和指针减法得到)。
正如Oli在评论中提到的,数组的类型确实包含长度信息,而std::end
使用数组引用作为参数,以避免衰减丢失此信息的指针。
定义基本上是:
namespace std
{
template<typename T, size_t N>
T* end(T (&array)[N])
{
return array + N;
// or if you want to be cute
// return 1[&array];
}
}
当数组引用中的边界是非类型模板参数时,它是可推导的。在编写接受数组的函数时,请使用此功能。
(编译器实际上对数组的内置结构进行了array + N
计算,但它并没有使用std::end()
。但是无论如何内联后都没有区别,看看你如何能够发挥编译器的相同技巧,这很有用。)