C++数组按引用传递的实现方式及相关语法问题详解
#include <iostream> template <class T> void call_1(T& in){ printf("%d\n", sizeof(in)); // 12 } template <int N> void call_2(int (&in)[N]){ printf("%d\n", sizeof(in)); // 12 } int main(){ int a[] = {1,2,3}; call_1(a); call_2(a); return 0; }
问题1:call_1和call_2是否均为将整个数组按引用传递的合法实现方式?
是,二者均是合法的数组按引用传递实现:
- call_2是显式固定数组元素类型为int的版本,模板参数
N会根据传入的数组长度自动推导,形参int (&in)[N]就是长度为N的int数组的引用,不会触发数组退化。 - call_1是通用版本,模板推导时会直接匹配数组的完整类型,此处传入
int[3]类型的数组a时,T会被推导为int[3],形参最终类型为int (&in)[3],和call_2的形参类型完全一致,同样不会触发数组退化。
二者运行时输出的sizeof(in)均为整个数组的大小12(3个4字节int),也验证了确实是按整个数组引用传递,而非传递指针。
问题2:call_1的参数声明中为何必须使用T&?如果省略&会传递数组首元素的指针,为何T&不会推导为数组首元素的引用,而是匹配整个数组的引用?
- 省略
&后,形参会变成值传递,C++中数组做值传递参数时会触发数组到指针的退化,数组名会被转换为首元素的指针,形参最终为T*类型,此时sizeof(in)得到的是指针的大小(32位环境下4、64位环境下8),无法拿到整个数组的大小。 - 当形参为左值引用类型
T&时,模板参数推导规则明确规定不会触发数组退化,会优先匹配实参的完整原始类型。此处实参a的原始类型就是int[3],没有经过任何类型转换,因此T会直接推导为int[3],形参就是整个数组的引用,不会退化为指针,也不会推导为单个元素的引用——因为单个int的引用和int[3]类型的实参类型不匹配,模板推导不会主动做这种类型截断。
问题3:call_2的模板声明中为何不能写template <T N>来避免硬编码int类型?该写法是否属于合法C++语法?
这个写法在当前上下文是非法的,原因是模板参数列表中使用的T没有被声明过,编译器无法识别T是什么类型。
如果要实现支持任意元素类型的通用数组引用传递,不需要硬编码int的正确写法是声明两个模板参数:
template <class T, size_t N> void call_2(T (&in)[N]){ printf("%d\n", sizeof(in)); }
其中T代表数组元素的类型,N代表数组长度,二者都可以通过传入的数组自动推导,不需要显式指定。
C++20之后新增了auto非类型模板参数语法,允许写template <auto N>,但该写法也不适合当前场景,因为你需要同时推导数组的元素类型和长度,还是需要单独声明元素类型参数T。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

