std::size 是首选方案,因它安全简洁、零开销、兼容c风格数组和std::array,编译期报错防误传指针,且语义统一于std::begin/std::end。

为什么 std::size 是首选方案
C++17 起,std::size(定义在 <iterator></iterator>)能安全、简洁地获取数组长度,且对 C 风格数组和 std::array 都有效。它本质是基于模板参数推导的重载函数,不依赖运行时信息,零开销。
手动写模板推导虽然可行,但容易出错,比如忽略引用限定或误匹配指针类型。而 std::size 已被标准库充分测试,兼容性好,还能避免常见陷阱:
- 对
int arr[5]返回size_t{5},不是int*的大小 - 不接受动态分配的指针(如
new int[5]),编译时报错,反而帮你提前发现问题 - 与
std::begin/std::end行为一致,语义统一
手动模板推导:必须用引用形参 如果非要自己写(比如需兼容 C++11 或定制逻辑),关键点在于:**数组名传入函数时会退化为指针,除非你用引用绑定原始类型**。
错误写法:template<typename t> void f(T arr)</typename> —— 此时 arr 是 T 类型值,无法得知长度。
正确写法(C++11 起):
template<typename t size_t n>
void f(T (&arr)[N]) {
// N 就是数组长度
static_assert(N > 0, "empty array not allowed");
}
</typename>
要点:
- 形参必须是
T (&arr)[N],即“对 T[N] 类型数组的引用”,&不能少 - 不能加
const在数组上(如const T (&arr)[N]仍可推导;但T const (&arr)[N]等价,没问题) - 若想支持
const数组,模板本身无需改,因为T会推导为const int等 - 该函数无法接收指针(如
f(ptr)编译失败),这是优点——防止误传动态内存
为什么不能用 sizeof(arr)/sizeof(arr[0]) 在函数内
很多人尝试在函数里写 sizeof(arr)/sizeof(arr[0]),但这只在数组定义作用域内有效。一旦作为参数传入(非引用方式),arr 就是退化后的指针,sizeof(arr) 返回指针大小(通常是 8),结果完全错误。
例如:
void bad_f(int arr[]) {
size_t n = sizeof(arr) / sizeof(arr[0]); // 永远是 8/4 = 2(64 位系统),不是原数组长度
}
所以这个技巧只适用于函数体内直接定义的数组,或宏(但宏有作用域和类型安全问题,不推荐)。
遇到 std::vector 或指针怎么办
std::size 对 std::vector 也有效(调用其 .size()),但要注意:它返回的是当前元素个数,不是容量;而对裸指针(如 int*)直接编译失败,不会静默返回错误值。
如果你的函数需要同时处理数组、std::vector 和指针(并附带长度),那就得重载或用概念(C++20)约束,或者干脆拆成不同接口。混用一种泛型签名去覆盖所有情况,往往导致逻辑复杂、边界模糊、调试困难。
最常被忽略的一点:数组长度是编译期常量,但很多实际场景中,你需要的是「运行时确定的元素个数」——这时候数组本身就不该是函数参数,而应由容器或显式长度参数承载。硬要靠模板推导“还原”长度,常常是设计倒置的信号。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











