模板技巧之传递数组参数

在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;
}

上面的模板函数,不仅用到了非类型参数 MN,还用到了类型参数 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