@当给一个函数传递数组相关的参数时,除了可以传递数组的起始地址,还可以传递整个数组的引用。
如:
void function(int (&arr)[10]);
此时,将函数的形参声明为一个数组的引用,这个数组的大小是10个int元素。
对于该函数的使用方法为:
int main(){
int i = 0, j[2] = {1,2};
int k[10] = {0--9};//伪代码
function(&i);//error
function(j);//error
function(k);//ok
}
可见,只有当传递的实参为一个大小为10的整形数组的名称时才能得到正确的编译结果。
@编译器内部的行为:
1. 编译器在这里不再接受一个对象实参,而是接受数组名,即数组的地址,其实,引用只不过是一个取对象地址,形成const 指针,对该const 指针解引用的过程
,此时,由于避开了取对象地址这一步(因为直接获取到了数组的地址),编译器需要做的就是将该指针在函数内部const 化,然后检测该指针的类型和数组的
大小与声明的是否匹配。
2.在函数内部使用形参时,对数组引用而言,实际上传递的是一个指针的引用,例如function(int (&arr)[10]),函数中传递的实际是数组名的引用,即指向数组首元素
的地址的指针,因此,在函数内部,必须像使用指针一样使用arr,如:
void function(int (&arr)[10]){
for(int i =0; i <10; i++)
arr[i] = i+1;
}从这里可以看出,编译器所需要做的其实很简单,检测arr指向的类型是不是一个int类型,数组的大小是不是10.因此可以说,这种形式与传递指针引用并无区别,唯一区
别开来的是,对指针引用的检查更加的严格了。
@当使用模板时,能够使用可变的指针类型和数组大小,如下:
template<typename T, size_t N> void function(T (&arr)[N]){
for(int i = 0; i < N; i++)
arr[i] = 0;
}
int main(){
int x[42];
double y[10];
function(x);//①
function(y);//②
}
①处编译器将函数模板实例化为function(int (&arr)[42])
②处编译器将函数模板实例化为function(double (&arr)[10])
@以下两种运用模板参数为引用的函数模板具有相同的效果:
template<typename T, int N1, int N2>
void fref(const T (&arr1)[N1], const T (&arr2)[N2]){
cout<<"A template with a reference of a array."<<endl;
}
template<typename T>
void fref_nonarray(const T &, const T &){
cout<<"A template with a reference."<<endl;
}
int main(int argc, char **argv){
int a[10] = {0};
int b[10] = {0};
int c[20] = {0};
fref(a,c);//①
fref_nonarray(a,b);//②
return 0;
}
在①处,可以将不同长度的数组作为模板实参传给模板,让编译器生成具体的函数实例。
在②处,需要将相同类型相同长度的数组传递给模板,此时编译器推断的参数为const T& ---->const int(&)[10],因此生成的具体的函数实例为:
void fref_nonarray(const int(&)[10], const int(&)[10]);