模板技巧之传递数组参数
在C/C++中,当需要把数组作为参数在函数间传递时,通常很麻烦。因为数组作为形参传递,会退化为指向数组首元素的指针。
int a[5] = {1,2,3,4,5};
cout << sizeof(a) << endl; // 20 = 5 * sizeof(int)
void foo(int arr[5]) {
for (int i = 0; i < 5; ++i) {
cout << arr[i] << endl;
}
cout << "sizeof(arr) = " << sizeof(arr) << endl; // 8 = sizeof(int*)
cout << begin(arr) << " " << end(arr) << endl; // err: no matching function for call
}在上面的函数中,虽然可以正常通过取下标的方式获取数组元素。但是,长度信息已经丢失了。传一个长度为3的数组就会越界。同时,sizeof也失去作用。
C语言的做法通常是连数组长度一块传参,
void foo(int* arr, size_t size) {
for (int i = 0; i < size; ++i) {
printf("arr[%d] = %d\n", i, arr[i]);
}
}但原本一个参数现在变两个,多少有点繁琐。如果要穿二维数组,那还得再多一个参数。
void foo(int** matrix, int row, int col);这样会给函数形参列表带来不小的增长,降低代码可读性。在C++中,可以通过传 vector 的引用来规避,或者用标准库的 array, 不过实际场景中 array 好像比较小众。
现在要介绍一种在C++中使用原生数组传参的技巧,借用模板函数,可以做到这一点。
template <int N>
int sqr_sum(int (&arr)[N]) {
int sum = 0;
for (int i = 0; i < N; i++)
sum += arr[i] * arr[i];
return sum;
}上面的模板函数,使用了非类型参数 N,保留了数组的长度信息。加上数组传引用是不会退化的,所以在函数内部,sizeof也能正常工作。再看一个二维数组的例子,
template <class T, int M, int N>
T cube_sum(const T (&a)[M][N]) {
T sum = 0;
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++)
sum += a[i][j] * a[j][i];
}
return sum;
}
int main() {
double f[2][2] = {{ 1.1, 2.2 }, { 3.3, 4.4 }};
printf("%lf\n", cube_sum(f));
return 0;
}上面的模板函数,不仅用到了非类型参数 M 和 N,还用到了类型参数 T.
注
int& arr[5]表示引用的数组。及数组有5个元素,每一个都是int&,而这在c++中是非法的。int (&arr)[5]表示数组的引用。即定义了一个引用,引用的类型是int[5].int[5]和int[6]是不同的基本类型!不可以互相赋值,传参,定义引用等。
参考:https://majorli.github.io/algo_guide/ch01/140_cpp_template.html