例如,在main函数中,我可以编写int arr[42]
,我可以使用range for循环而不指示其长度for (auto i : arr)
。它运作良好。编译器会知道它的长度。
如果我将此数组传递给另一个函数,例如void foo(int arr[])
,我就不能使用range for循环,因为编译器不知道它的长度。
毕竟,如果我有这个模板template<class T, size_t N> void arr_init(T (&foo)[N])
,我可以通过arr_init(x)
来调用它。显然,编译器调用arr_init(int (&)[42])
。
我猜编译器通过声明int[42]
知道数组的长度。我对吗?当我使用range for loop并学习模板时,我有这个问题。当我使用C时,我还没有遇到过这样的问题。
答案 0 :(得分:2)
数组类型T[N]
与类型T[M]
的数组类型T
以及两个不同大小N
和M
不同。因此,数组的长度内置在类型中。
OTOH,数组自动decayed指向函数调用参数列表中的指针。换句话说,这三个功能签名之间没有区别:
int func(int []);
int func(int[5]);
int func(int[10]);
int func(int*);
以上所有签名都只是int*
。如果要保留数组类型,则需要创建一个以数组引用作为参数的函数。这正是您在示例中template<class T, size_t N> void arr_init(T (&foo)[N])
所实现的。在arr_init
内,foo
的行为类似于数组,因为它是对数组类型的引用。
对于arr
类型的数组T[N]
,range-based for
分别使用表达式arr
和arr+N
作为起点和终点。
对于类类型,成员函数begin
和end
(如果存在)用于确定端点,非成员重载失败,begin
和end
使用。
(begin
和end
,而不是std::begin
和std::end
,因为标准中begin
和end
的一般用法图书馆是这样的:
using std::begin;
begin(...);
using std::end;
end(...);
这允许ADL查找用户定义的begin
和end
重载。)
答案 1 :(得分:1)
是的,你是对的。这是因为编译器知道在同一个翻译单元中定义的对象的大小。
事实上,您不需要使用范围for循环来查看类似的行为;你可以使用普通sizeof
。
foo.c的:
#include <stdio.h>
extern int a[];
int main()
{
printf("%d\n", sizeof(a));
}
bar.c:
int a[50];
现在测试:
gcc foo.c bar.c
> foo.c: In function 'main':
> foo.c:7:23: error: invalid application of 'sizeof' to incomplete type 'int[]'
答案 2 :(得分:0)
每当您通过引用传递数组时,您可以推断它的大小(请记住,数组不是指针)。因此,基于范围的for使用的函数std::begin()
和std::end()
可能像数组一样重载
namespace std
{
template<typename T, std::size_t N>
T* std::begin(T (&arr)[N])
{
return arr;
}
template<typename T, std::size_t N>
T* std::end(T (&arr)[N])
{
return arr + N;
}
}
只有在按值传递时,数组才会衰减为指针,如
void f(int arr[256]){...}
是
的语法糖void f(int* arr){...}
PS:看起来基于范围的for
不使用std::begin
和std::end
,因为编译器在编译时知道数组的大小(如@所述) TC)。所以上面的代码仅用于演示目的;)
有关基于范围的for
的详细信息,请参阅标准的 6.5.4 :
在每种情况下,基于范围的for语句等同于
{
auto && __range = range-init;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}
6.4.3 / 1 如果_RangeT是数组类型,则 begin-expr 和 end-expr __ range 和 __ range + __bound ,其中 __ bound 是数组绑定的。如果 _RangeT 是未知大小的数组或类型不完整的数组,则该程序格式不正确;