不能直接写 const int& arr,因为它是对单个int的const引用,而非对整个数组的引用;正确做法是用模板推导数组长度template void process(const int (&arr)[N])或C++20的std::span。

为什么不能直接写 const int& arr
这是初学者最常卡住的地方:把数组名当普通变量传,结果编译器报错 cannot bind non-const lvalue reference to an rvalue 或更迷惑的 invalid initialization of reference of type ... from expression of type ...。根本原因在于,const int& 是“对单个 int 的 const 引用”,不是“对整个数组的引用”。C++ 不允许隐式将数组退化为指针后再绑定到引用上——尤其当你还想保留长度信息时。
正确写法:带长度的模板推导(推荐)
最安全、最通用的方式是用模板捕获数组大小:
template <size_t N>
void process(const int (&arr)[N]) {
// arr 是 const 引用,类型为 const int([N])
// sizeof(arr) == N * sizeof(int),长度没丢
for (size_t i = 0; i < N; ++i) {
// 可读,不可写:arr[i] = 42; // ❌ 编译错误
}
}调用时直接传数组名即可:process(my_array);。编译器自动推导 N,且拒绝传入指针(比如 process(ptr) 会编译失败),天然防误用。
- 适用于栈上定义的数组(如
int a[5]),不适用于new int[5]或函数参数里已退化的数组 - 如果函数需要支持不同元素类型,把
int换成模板参数typename T即可 - 注意:不能用于
std::vector或动态分配数组——它们不是 C 风格数组
替代方案:std::span<const int></const>(C++20 起)
如果你需要处理任意连续内存(包括 vector、动态数组、子数组切片),std::span 是现代 C++ 的标准解法:
立即学习“C++免费学习笔记(深入)”;
void process(std::span<const int> span) {
for (const auto& x : span) { /* ... */ }
// span.size() 给出长度,span.data() 获取原始指针
}它接受多种输入:
-
process(my_array);// 自动推导长度 -
process(std::vector{1,2,3});// 从 vector 构造 -
process(std::span{ptr, len});// 显式指定指针+长度
关键点:std::span 本身不拥有数据,只是轻量视图;const int 模板参数保证只读语义;无运行时开销,和裸指针+长度一样快。
常见陷阱与绕不开的限制
有些做法看似可行,实则危险或失效:
-
void f(const int* arr):不是引用传递,无法阻止数组退化,调用者仍可传nullptr,函数内也拿不到长度 -
void f(const std::array<int>& arr)</int>:可行,但必须硬编码长度 5,失去泛型能力 -
void f(int const (&arr)[]):语法错误!C++ 不允许声明不带长度的数组引用类型 - 试图在函数参数中用
auto&推导数组类型:C++17 起仍不支持,auto&对数组名会退化为指针引用
真正难的是让接口既安全又灵活——模板推导适合明确知道是栈数组的场景,std::span 是更通用的答案,但要求 C++20 支持。如果项目还卡在 C++11/14,老老实实用模板 + size_t N 参数组合,别为了“省一个参数”去绕弯子。


















