在《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