1. 数组和std::array
std::array
是C++容器库提供的一个固定大小数组的容器。其与内置的数组相比,是一种更安全、更容易使用的数组类型。std::array
在头文件<array>中定义,其声明如下:
template<
class T,
std::size_t N
> struct array; //C++11 起
std::array
是一个聚合类型,其语义等同于保有一个C语言风格数组T[N]
作为其唯一非静态数据成员的结构体,但其不同于C数组的是它不会自动退化为T*
。同时该结构体结合了C风格数组的性能、可访问性和容器的优点(可获取大小、支持赋值和随机访问等)。
2. array的用法
2.1 成员函数
2.1.1 隐式定义的成员函数
构造函数(隐式声明) | 遵循聚合初始化的规则初始化 array(注意默认初始化可以导致非类的T的不确定值) |
析构函数(隐式声明) | 销毁 array 的每个元素 |
operator=(隐式声明) | 以来自另一 array的每个元素重写array的对应元素 |
聚合初始化就是从初始化器列表来初始化聚合体,其也是列表初始化的一种方式。
std::array<int, 3> a = {1,2,3};
std::array<int, 3> b;
b = a; //将a中的每个元素重写到b中,使用operator=时候需要确保a b两个容器长度相等,否则编译失败
2.1.2 元素访问
at
at用于访问指定的元素,同时进行越界检查,该函数返回位于指定位置pos的元素的引用,如果pos不在容器的范围内,则抛出std::out_of_range
异常。其函数声明如下:
reference at( size_type pos ); //C++17 前
constexpr reference at( size_type pos ); //C++17 起
const_reference at( size_type pos ) const; //C++14 前
constexpr const_reference at( size_type pos ) const; //C++14 起
其具体用法如下:
std::array<int,3> data = { 1, 2, 3};
std::cout<<data.at(1)<<std::endl; //2
data.at(1)=8; //此时data={1, 8, 3}
data.at(6) = 6; //越界,抛出std::out_of_range异常
operator[]
operator[]与at功能相同,即用来访问指定的元素,但其与at不同的是:operator[]不进行边界的检查。其函数声明如下所示:
reference operator[]( size_type pos ); //C++17 前
constexpr reference operator[]( size_type pos ); //C++17 起
const_reference operator[]( size_type pos ) const; //C++14 前
constexpr const_reference operator[]( size_type pos ) const; //C++14 起
注:通过operator[]符访问不存在的元素是未定义行为。
front
front用于访问容器的第一个元素,其返回值为容器首元素的引用,其函数原型如下:
reference front(); //C++17 前
constexpr reference front(); //C++17 起
const_reference front() const; //C++14 前
constexpr const_reference front() const; //C++14 起
注:在空容器上对 front
的调用是未定义的。
back
back主要功能是用来访问容器最后一个元素,其返回值为容器最后一个元素的引用,其函数原型如下所示:
reference back(); //C++17 前
constexpr reference back(); //C++17 起
const_reference back() const; //C++14 前
constexpr const_reference back() const; //C++14 起
注:在空容器上调用 back
导致未定义行为。
data
data可以直接访问容器底层数组,其返回值为指向作为元素存储工作的底层数组的指针。其函数声明如下:
T* data() noexcept; //C++11 起, C++17 前
constexpr T* data() noexcept; //C++17 起
const T* data() const noexcept; //C++11 起, C++17 前
constexpr const T* data() const noexcept; //C++17 起
其返回的指针使得范围[ data()
, data() + size()
)始终是合法范围。
2.2.3 迭代器
begin、end和cbegin、cend
begin和cbegin返回指向deque首元素的迭代器,end和cend返回指向deque末元素后一元素的迭代器。其函数声明如下:
iterator begin() noexcept; //C++17 前
constexpr iterator begin() noexcept; //C++17 起
const_iterator begin() const noexcept; //C++17 前
constexpr const_iterator begin() const noexcept; //C++17 起
const_iterator cbegin() const noexcept; //C++17 前
constexpr const_iterator cbegin() const noexcept; //C++17 起
iterator end() noexcept; //C++17 前
constexpr iterator end() noexcept; //C++17 起
const_iterator end() const noexcept; //C++17 前
constexpr const_iterator end() const noexcept; //C++17 起
const_iterator cend() const noexcept; //C++17 前
constexpr const_iterator cend() const noexcept; //C++17 起
如果array为空,则返回的迭代器将等于end或cend。end和cend指向deque末元素后一元素的迭代器,该元素的表现为占位符,试图访问它将导致未定义行为。
rbegin、rend和crbegin、crend
rbegin和crbegin返回指向array首元素的逆向迭代器。它对应非逆向array的末元素,若array为空,则返回的迭代器等于rend或crend。rend和crend返回指向逆向deque末元素后一元素的逆向迭代器,它对应非逆向array首元素的前一元素,此元素表现为占位符,试图访问它导致未定义行为。它们的声明如下:
reverse_iterator rbegin() noexcept; //C++17 前
constexpr reverse_iterator rbegin() noexcept; //C++17 起
const_reverse_iterator rbegin() const noexcept; //C++17 前
constexpr const_reverse_iterator rbegin() const noexcept; //C++17 起
const_reverse_iterator crbegin() const noexcept; //C++17 前
constexpr const_reverse_iterator crbegin() const noexcept; //C++17 起
reverse_iterator rend(); //C++11 前
reverse_iterator rend() noexcept; //C++11 起
const_reverse_iterator rend() const; //C++11 前
const_reverse_iterator rend() const noexcept; //C++11 起
const_reverse_iterator crend() const noexcept; //C++11 起
2.2.4 容量
empty
empty用来检查容器是否为空,若为空则返回true,否则为false。其函数声明如下:
constexpr bool empty() const noexcept; //C++11 起,C++20 前
[[nodiscard]] constexpr bool empty() const noexcept; //C++20 起
其底层实现就是检查容器是否无元素,即判断是否begin() == end()
。
size
size函数返回容器中元素数量,即std::distance(begin(), end())
。其函数声明如下:
constexpr size_type size() const noexcept; //C++11 起
max_size
max_size函数返回根据系统或库实现限制的容器可保有的元素最大数量,即对于最大容器的 std::distance(begin(), end())
。其函数声明为:
constexpr size_type max_size() const noexcept; //C++11 起
注:因为每个std::array<T, N>
都是固定大小容器,故max_size
返回的值等于N
(亦为size所返回的值)
2.2.5 修改器
fill
fill函数原型如下所示:
void fill( const T& value ); //C++11 起, C++20 前
constexpr void fill( const T& value ); //C++20 起
fill函数主要用于以指定值填充容器,即将定值 value
赋给容器中的所有元素。
具体用法示例如下:
std::array<int, 3> arr = {1, 2, 3};
arr.fill(1); // arr = {1, 1, 1}
swap
swap函数的主要作用是交换两个array容器的内容,其与deque的swap不同的是不导致迭代器和引用关联到别的容器。其函数声明如下:
void swap( array& other ) noexcept(); //C++11 起, C++20 前
constexpr void swap( array& other ) noexcept(); //C++20 起
其用法示例如下图所示:
std::array<int, 3> a1{1, 2, 3}, a2{4, 5, 6};
auto it1 = a1.begin(); //*it1 = 1
auto it2 = a2.begin(); //*it2 = 4
int &ref1 = a1[1]; // ref1 = 2
int &ref2 = a2[1]; // ref1 = 5
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印结果为1 4 2 5
a1.swap(a2);
// 此时a1 = {4, 5, 6},a2 = {1, 2, 3}
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印结果为4 1 5 2
/*注:
交换后迭代器与引用保持与原 array 关联,
例如it1仍指向元素 a1[0] ,ref1仍指代 a1[1] */
2.2 非成员函数
operator==,!=,<,<=,>,>=,<=>(std::array)
C++提供operator==,!=,<,<=,>,>=,<=>(std::array)
非成员函数用来比较两个array的大小,相关函数及函数声明如下:
//1. ==
//返回值:在 array 内容相等时返回 true,否则返回 false
template< class T, std::size_t N >
bool operator==( const std::array<T, N>& lhs,
const std::array<T, N>& rhs ); //C++20 前
template< class T, std::size_t N >
constexpr bool operator==( const std::array<T, N>& lhs,
const std::array<T, N>& rhs ); //C++20 起
//2. !=
//返回值:在 array 内容不相等时返回 true,否则返回 false
template< class T, std::size_t N >
bool operator!=( const std::array<T, N>& lhs,
const std::array<T, N>& rhs ); //C++20 前
//3. <
//返回值:在 lhs 的内容按字典序小于 rhs 的内容时返回 true,否则返回 false
template< class T, std::size_t N >
bool operator<( const std::array<T, N>& lhs,
const std::array<T, N>& rhs ); //C++20 前
//4. <=
//返回值:在 lhs 的内容按字典序小于或等于 rhs 的内容时返回 true,否则返回 false
template< class T, std::size_t N >
bool operator<=( const std::array<T, N>& lhs,
const std::array<T, N>& rhs ); //C++20 前
//5. >
//返回值:在 lhs 的内容按字典序大于 rhs 的内容时返回 true,否则返回 false
template< class T, std::size_t N >
bool operator>( const std::array<T, N>& lhs,
const std::array<T, N>& rhs ); //C++20 前
//6. >=
//返回值:在 lhs 的内容按字典序大于或等于 rhs 的内容时返回 true,否则返回 false
template< class T, std::size_t N >
bool operator>=( const std::array<T, N>& lhs,
const std::array<T, N>& rhs ); //C++20 前
//7. <=>
//返回值:lhs 与 rhs 中的首对不等价元素的相对顺序,如果有这种元素;否则是 lhs.size() <=> rhs.size()。
template< class T, std::size_t N >
constexpr operator<=>( const std::array<T, N>& lhs,
const std::array<T, N>& rhs ); //C++20 起
1,2中会检查lhs和rhs的内容是相等,即他们是否拥有相同数量的元素且lhs中每个元素与rhs的相同位置元素比较相等。同时函数中T
必须符合可相等比较 (EqualityComparable) 的要求
3-6中按照字典比较lhs和rhs的内容,其内部等价于调用std::lexicographical_compare
函数进行比较。同时函数中T
必须符合[可小于比较 (LessThanComparable) 的要求。
7中也是按字典序比较lhs和rhs的内容。其内部等价于调用std::lexicographical_compare_three_way
进行比较。返回类型同合成三路比较的结果类型。其逻辑大致如下:
lhs < rhs ? std::weak_ordering::less :
rhs < lhs ? std::weak_ordering::greater :
std::weak_ordering::equivalent
//注:通常情况下less对应的是-1,greater对应1,equivalent对应0
lhs与rhs中的首对不等价元素的相对顺序,如果有这种元素;否则是 lhs.size() <=> rhs.size()
。
其具体的应用示例如下所示:
std::array<int, 3> alice{1, 2, 3};
std::array<int, 3> bob{7, 8, 9};
std::array<int, 3> eve{1, 2, 3};
std::cout << std::boolalpha;
// 比较不相等的容器
std::cout << "alice == bob returns " << (alice == bob) << '\n';
std::cout << "alice != bob returns " << (alice != bob) << '\n';
std::cout << "alice < bob returns " << (alice < bob) << '\n';
std::cout << "alice <= bob returns " << (alice <= bob) << '\n';
std::cout << "alice > bob returns " << (alice > bob) << '\n';
std::cout << "alice >= bob returns " << (alice >= bob) << '\n';
std::cout << '\n';
// 比较相等的容器
std::cout << "alice == eve returns " << (alice == eve) << '\n';
std::cout << "alice != eve returns " << (alice != eve) << '\n';
std::cout << "alice < eve returns " << (alice < eve) << '\n';
std::cout << "alice <= eve returns " << (alice <= eve) << '\n';
std::cout << "alice > eve returns " << (alice > eve) << '\n';
std::cout << "alice >= eve returns " << (alice >= eve) << '\n';
输出结果为
alice == bob returns false
alice != bob returns true
alice < bob returns true
alice <= bob returns true
alice > bob returns false
alice >= bob returns false
alice == eve returns true
alice != eve returns false
alice < eve returns false
alice <= eve returns true
alice > eve returns false
alice >= eve returns true
std::get(std::array)
std::get(std::array)
可以用来访问array的一个元素,其函数声明如下:
template< std::size_t I, class T, std::size_t N >
T& get( std::array<T,N>& a ) noexcept; //C++11 起, C++14 前
template< std::size_t I, class T, std::size_t N >
constexpr T& get( std::array<T,N>& a ) noexcept; //C++14 起
template< std::size_t I, class T, std::size_t N >
T&& get( std::array<T,N>&& a ) noexcept; //C++11 起, C++14 前
template< std::size_t I, class T, std::size_t N >
constexpr T&& get( std::array<T,N>&& a ) noexcept; //C++14 起
template< std::size_t I, class T, std::size_t N >
const T& get( const std::array<T,N>& a ) noexcept; //C++11 起, C++14 前
template< std::size_t I, class T, std::size_t N >
constexpr const T& get( const std::array<T,N>& a ) noexcept; //C++14 起
template< std::size_t I, class T, std::size_t N >
const T&& get( const std::array<T,N>&& a ) noexcept; //C++11 起, C++14 前
template< std::size_t I, class T, std::size_t N >
constexpr const T&& get( const std::array<T,N>&& a ) noexcept; //C++14 起
其主要作用是从a
中提取第I
个元素.I
必须是范围 [0, N)
中的整数值。与at()
或 operator[]
相反,这在编译时强制。该函数的返回值为a
中第I
元素的引用。
其具体的用法如下:
std::array<int, 3> arr;
// 设置值:
std::get<0>(arr) = 1;
std::get<1>(arr) = 2;
std::get<2>(arr) = 3;
// 获取值:
std::cout << "(" << std::get<0>(arr) << ", " << std::get<1>(arr)
<< ", " << std::get<2>(arr) << ")\n";
//输出结果为 (1, 2, 3)
std::swap(std::array)
std::swap(std::array)
函数是为std::array
特化std::swap
算法。其函数声明如下:
template< class T, std::size_t N >
void swap( std::array<T, N>& lhs,
std::array<T, N>& rhs ); //C++11 起, C++17 前
template< class T, std::size_t N >
void swap( std::array<T, N>& lhs,
std::array<T, N>& rhs ) noexcept(); //C++17 起, C++20 前
template< class T, std::size_t N >
constexpr void swap( std::array<T, N>& lhs,
std::array<T, N>& rhs ) noexcept(); //C++20 起
交换 lhs
与 rhs
的内容。调用lhs.swap(rhs)
。其具体用法如下:
std::array<int, 3> a1{1, 2, 3}, a2{4, 5, 6};
auto it1 = a1.begin(); //*it1 = 1
auto it2 = a2.begin(); //*it2 = 4
int &ref1 = a1[1]; // ref1 = 2
int &ref2 = a2[1]; // ref1 = 5
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印结果为1 4 2 5
std::swap(a1, a2);
// 此时a1 = {4, 5, 6},a2 = {1, 2, 3}
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印结果为4 1 5 2
std::to_array
std::to_array
函数声明如下:
template<class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N]); //C++20 起
template<class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&&a)[N]); //C++20 起
std::to_array
函数可以从一维内建数组 a
创建 std::array
对象,从 a
的对应元素复制初始化 std::array
的元素。不支持复制或移动多维内建数组。其具体用法如下:
#include <array>
#include <iostream>
int main()
{
// 复制字符串字面量
auto a1 = std::to_array("foo");
static_assert(a1.size() == 4);
// 推导元素类型和长度
auto a2 = std::to_array({0, 2, 1, 3});
// 推导长度而元素类型指定
// 发生隐式转换
auto a3 = std::to_array<long>({0, 1, 3});
auto a4 = std::to_array<std::pair<int, float>>(
{{3, .0f}, {4, .1f}, {4, .1e23f}});
// 创建不可复制的 std::array
auto a5 = std::to_array({std::make_unique<int>(3)});
// 错误:不支持复制多维数组
// char s[2][6] = { "nice", "thing" };
// auto a6 = std::to_array(s);
}
std::tuple_size
std::tuple_size(std::array)
函数的声明如下:
template< class T, std::size_t N >
struct tuple_size< std::array<T, N> > :
std::integral_constant<std::size_t, N> //C++11 起
{ };
其提供作为编译时常量表达式访问std::array
中元素数量的方法。用法示例如下:
#include <iostream>
#include <array>
template<class T>
void test(T t)
{
int a[std::tuple_size<T>::value]; // 能用于编译时
std::cout << std::tuple_size<T>::value << '\n';
}
int main()
{
std::array<float, 3> arr;
test(arr); //输出 3
}
std::tuple_element
std::tuple_element<std::array>
函数主要用来获得 array
元素的类型,其声明如下:
template< std::size_t I, class T, std::size_t N >
struct tuple_element<I, std::array<T, N> >; //C++11 起
其使用类 tuple 接口,提供 array 元素类型的编译时带下标访问。具体使用方法如下:
// 定义 array 并获取位于位置 0 的元素类型
std::array<int, 10> data {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
using T = std::tuple_element<0, decltype(data)>::type; // int
3. 总结
数组std::array
的优劣:
优点
- 无开销随机访问。
- 快速遍历;适合线性搜索。
劣势
- 如果元素类型具有较高的复制/分配成本,则可能会变慢(重新排序元素需要复制/移动它们)。
- 在使用array容器的时候,其size必须是常量表达式(即编译时已知)。
- 不支持大小更改操作(调整大小、插入、擦除等)。