在《std::vector 与无符号长度及下标问题》中,我们讨论了标准库容器将长度与索引设为无符号类型所带来的困境。由于 std::array 也属于标准库容器,因此同样面临这一问题。本节将回顾如何获取 std::array 的长度并进行索引。由于 std::vector 与 std::array 接口相似,内容将与前述讨论保持一致;但因只有 std::array 完全支持 constexpr,我们将重点探讨这一点。
开始前,请先复习“符号转换属于收缩转换,except 当 constexpr”这一要点(见《std::vector 与无符号长度及下标问题》)。
std::array 的长度类型为 std::size_t
std::array 的实现如下:
template<typename T, std::size_t N> // N 为非类型模板参数
struct array;
可见,表示长度的非类型模板参数 N 类型为 std::size_t,这是一个大型无符号整数类型。
相关内容:
- 类模板(含结构模板)
- 非类型模板参数
因此,定义 std::array 时,长度实参必须为 std::size_t 或可转换为 std::size_t 的常量表达式。由于该值必须是 constexpr,若使用带符号整数,编译期即可安全转换为 std::size_t,不构成收缩转换。
旁注:
C++23 前无std::size_t字面量后缀,因编译期int → std::size_t转换已足够;C++23 引入UZ后缀,便于区分0(int)与0UZ(std::size_t)。
std::array 的长度与索引类型 size_type 始终为 std::size_t
与 std::vector 一样,std::array 提供嵌套 typedef size_type,即容器长度/索引的别名。对于 std::array,size_type 始终是 std::size_t 的别名。
注意:长度模板参数显式使用 std::size_t 而非 size_type,因为 size_type 此时尚未定义;其余位置均使用 size_type。
获取 std::array 长度的三种方式
- 成员函数 - size()
 返回无符号- size_type长度:- #include <array> #include <iostream> int main() { constexpr std::array arr{9.0, 7.2, 5.4, 3.6, 1.8}; std::cout << "length: " << arr.size() << '\n'; // 返回 size_type }- 输出: - length: 5- 与 - std::string/- std::string_view不同,- std::array仅提供- size(),无- length()。
- C++17 非成员函数 - std::size()
 对- std::array内部调用- size(),返回无符号- size_type:- std::cout << "length: " << std::size(arr);
- C++20 非成员函数 - std::ssize()
 返回带符号长度(通常为- std::ptrdiff_t):- std::cout << "length: " << std::ssize(arr);
以 constexpr 方式获取长度
由于 std::array 长度是 constexpr,上述函数即使对非 constexpr 对象也返回 constexpr 值,可在常量表达式中使用:
std::array arr{9, 7, 5, 3, 1}; // 非常量对象
constexpr int len{std::size(arr)}; // OK:返回 constexpr std::size_t,可安全转为 int
Visual Studio 误报 C4365;已向微软反馈。
语言缺陷:若
std::array作为函数参数按(const)引用传递,上述函数返回非 constexpr 值。C++23 的 P2280 已修正,当前编译器支持有限。
解决:将函数改为模板,以非类型模板参数接收长度。
索引方式
1. operator[] 与 at()
- operator[]:无边界检查,越界即 UB。
- at():运行时边界检查,越界抛出- std::out_of_range。
 推荐避免- at(),因我们通常提前检查或需要编译期检查。
二者均期望索引类型为 size_type(std::size_t)。
- constexpr索引:编译期转换,无符号问题。
- 非 constexpr带符号索引:可能触发符号转换警告。
2. std::get<I>() —— 编译期边界检查
当索引为 constexpr 时,可用 std::get<I>() 模板函数:
constexpr std::array prime{2, 3, 5, 7, 11};
std::cout << std::get<3>(prime); // OK:7
std::cout << std::get<9>(prime); // 编译错误:越界
内部含 static_assert,越界即停止编译。std::get 只能用于 constexpr 索引。
小测验
问题 1
用 std::array 初始化字符 'h','e','l','l','o':
- 输出数组长度;
- 分别用 operator[]、at()、std::get()打印下标 1 的元素。
期望输出:
The length is 5
eee
