C++ 具名要求:序列容器 (SequenceContainer)

来自cppreference.com
< cpp‎ | named req


 
 
C++ 具名要求
 

序列容器 (SequenceContainer) 是在线性排列中存储相同类型对象的容器 (Container)

要求

凡例
X 序列容器类
T X 的元素类型
a X 类型的值
u 被声明的变量名
A X 的分配器类型:
  • X::allocator_type 存在时是该类型,
  • 否则是 std::allocator<T>
ij 老式输入迭代器 (LegacyInputIterator) [ij)有效范围,并且这些迭代器所指代的元素可隐式转换到 value_type
rg (C++23 起) 实现了 container-compatible-range<T> 的类型 R 的值
il (C++11 起) std::initializer_list<value_type> 类型的值
n X::size_type 类型的值
p a 中的有效常量迭代器
q a 中的有效可解引用常量迭代器
q1q2 a 中的两个常量迭代器,使得 [q1q2) 是有效范围
t X::value_type 类型的左值或 const 右值 (C++11 起)
rv (C++11 起) X::value_type 类型的非 const 右值
Args (C++11 起) 模板形参包
args (C++11 起) 模式为 Args&& 的函数形参包

在满足以下所有条件时,X序列容器 (SequenceContainer)

  • 类型 X 满足容器 (Container)
  • 下列语句和表达式必须合法,且对std::array 之外的(见注解 (C++11 起)所有序列容器均具有所指定的效果:
语句 效果     条件[1]
X u(n, t) 构造保有 nt 的副本的序列容器 T 可复制插入 (CopyInsertable) X
std::distance(u.begin(), u.end())
    == n
X u(i, j) 构造与范围 [ij) 逐元素相等的序列容器 T*i 可就位构造 (EmplaceConstructible) X
std::distance(u.begin(), u.end())
    == std::distance(i, j)
表达式 类型 效果 条件
X(std::from_range, rg)
(C++23 起)
X 构造与范围 rg 逐元素相等的序列容器 T*ranges::begin(rg) 可就位构造 (EmplaceConstructible) X
X(il)
(C++11 起)
X 等价于 X(il.begin(),
    il.end())
无明确要求
a = il
(C++11 起)
X& il 所表示的范围赋值到 a 中。[2] T 可复制插入 (CopyInsertable) 可复制赋值 (CopyAssignable)
a 的既存元素被销毁或被赋值
a.emplace(p, args)
(C++11 起)
iterator p 前插入以 std::forward<Args>
    (args)
构造的 T 类型的对象
T 可复制插入 (CopyInsertable)
返回的迭代器指向由 args 构造到 a 中的元素
a.insert(p, t) iterator p 前插入 t 的副本 T 可复制插入 (CopyInsertable)
返回的迭代器指向插入到 a 中的 t 的副本
a.insert(p, rv)
(C++11 起)
iterator p 前插入 rv 的副本,可能使用移动语义 T 可移动插入 (MoveInsertable)
返回的迭代器指向插入到 a 中的 rv 的副本
a.insert(p, n, t) iterator 插入 nt 的副本到 p 之前 T 可复制插入 (CopyInsertable) 可复制赋值 (CopyAssignable)
返回的迭代器指向插入到 a 中的首元素的副本,或当 n == 0 时返回 p
a.insert(p, i, j) iterator 插入 [ij) 中元素的副本到 p 之前 T 可就位构造 (EmplaceConstructible) ij 不在 a
  • [ij) 中的每个迭代器都只会解引用一次
  • 返回的迭代器指向插入到 a 的首元素的副本,或当 i == j 时返回 p
a.insert_range(p, rg)
(C++23 起)
iterator 插入 rg 中元素的副本到 p 之前
  • rg 中的每个迭代器都只会解引用一次
  • 返回的迭代器指向插入到 a 的首元素的副本,或当 rg 为空时返回 p
a.insert(p, il)
(C++11 起)
iterator 等价于 a.insert(p,
         il.begin(),
         il.end())
无明确要求
返回的迭代器指向插入到 a 中的首元素的副本,或当 il 为空时返回 p
a.erase(q) iterator 擦除 q 指向的元素 无明确要求
返回的迭代器指向擦除前紧跟 q 之后的元素,或在此类元素不存在时返回 a.end()
a.erase(q1, q2) iterator 擦除 [q1q2) 中的元素 无明确要求
返回的迭代器指向在任何擦除前 q2 曾指向的元素,或在此类元素不存在时返回 a.end()
a.clear() void 销毁 a 中所有元素 无明确要求
  • 所有引用、指针和迭代器都会失效,包含尾迭代器
  • a.empty()true
a.assign(i, j) void [ij) 的副本替换 a 中的元素
[ij) 中的每个迭代器都只会解引用一次
a.assign_range(rg)
(C++23 起)
void rg 中每个元素的副本替换 a 中的元素
  • rg 中的每个迭代器都只会解引用一次
  • 所有引用、指针和迭代器都会失效
a.assign(il)
(C++11 起)
void 等价于

a.assign(il.begin(),
         il.end())

无明确要求
a.assign(n, t) void tn 个副本替换 a 中的元素 T 可复制插入 (CopyInsertable) 可复制赋值 (CopyAssignable)
无明确要求
  1. 对于效果等价于其他操作的表达式,在表格中列出的条件的基础上也会继承那些操作中的表达式的条件。
  2. std::array 支持从花括号初始化式列表赋值,但不支持从 std::initializer_list 赋值
  3. TR 使得 std::assignable_from<T&, ranges::range_reference_t<R>> 得到实现的类型。

可选操作

下列表达式必须合法,且对于所指名的序列容器拥有所指定的效果,prepend_rangeappend_range 以外的 (C++23 起)所有操作都会在均摊常数时间内完成:

表达式 类型 效果     前条件[1] 容器
a.front() reference

对于 const aconst_reference

返回 *a.begin() 无明确要求 std::basic_string std::array std::deque std::forward_list std::inplace_vector std::list std::vector
a.back() reference

对于 const aconst_reference

等价于 auto tmp = a.end();
--tmp;
return *tmp;
无明确要求 std::basic_string std::array std::deque std::inplace_vector std::list std::vector
a.emplace_front(args)
(C++11 起)
void 前附一个以 std::forward<Args>
    (args)...
构造的 T
Targs 可就位构造 (EmplaceConstructible) X std::deque std::forward_list std::list
a.emplace_back(args)
(C++11 起)
void 后附一个以 std::forward<Args>
    (args)...
构造的 T
Targs 可就位构造 (EmplaceConstructible) X std::deque std::inplace_vector std::list std::vector
a.push_front(t) void 前附 t 的一个副本 T 可复制插入 (CopyInsertable) X std::deque std::forward_list std::list
a.push_front(rv)
(C++11 起)
void 前附 rv 的一个副本,可能用移动语义 T 可移动插入 (MoveInsertable) X
a.prepend_range(rg)
(C++23 起)
void 插入[2] rg 中的元素的副本到 begin() 前,rg 中的每个迭代器都只会解引用一次 T*ranges::begin(rg) 可就位构造 (EmplaceConstructible) X std::deque std::forward_list std::list
a.push_back(t) void 后附 t 的一个副本 T 可复制插入 (CopyInsertable) X std::basic_string std::deque std::inplace_vector std::list std::vector
a.push_back(rv)
(C++11 起)
void 后附 rv 的一个副本,可能用移动语义 T 可移动插入 (MoveInsertable) X
a.append_range(rg)
(C++23 起)
void 插入[2] rg 中的元素的副本到 end() 前,rg 中的每个迭代器都只会解引用一次 T*ranges::begin(rg) 可就位构造 (EmplaceConstructible) X std::deque std::inplace_vector std::list std::vector
a.pop_front() void 销毁首元素 a.empty()false std::deque std::forward_list std::list
a.pop_back() void 销毁最末元素 a.empty()false std::basic_string std::deque std::inplace_vector std::list std::vector
a[n] reference

对于 const aconst_reference

返回 *(a.begin() + n) 无明确要求 std::basic_string std::array std::deque std::inplace_vector std::vector
a.at(n) reference

对于 const aconst_reference

返回 *(n + a.begin()),在 n >= size() 时会抛出 std::out_of_range 无明确要求
  1. 对于效果等价于其他操作的表达式,在表格中列出的前条件的基础上也会继承那些操作中的表达式的前条件。
  2. 2.0 2.1 插入顺序就是 rg 中元素的顺序,不是逆序。

另外,对于每个序列容器:

  • 对于接收两个输入迭代器的构造函数模板,以及成员函数 insert()append()assign()replace() 的接受两个输入迭代器的模板重载,如果相应的模板实参不是老式输入迭代器 (LegacyInputIterator) ,那么它们不参与重载决议。
  • 对于拥有 老式输入迭代器 (LegacyInputIterator) Allocator 模板形参的推导指引,如果该形参推导出的类型(分别)不是输入迭代器或分配器,那么它们不参与重载决议。
(C++17 起)

标准库中的序列容器

存储并操作字符序列
(类模板)
(C++11)
固定大小的原位连续数组
(类模板)
动态的连续数组
(类模板)
可动态调整大小的固定容量原位连续数组
(类模板)
双端队列
(类模板)
(C++11 起)
单链表
(类模板)
双链表
(类模板)

权衡/使用备注

std::vector 快速访问,但大多情况的插入/删除低效。
std::inplace_vector 快速访问,原位连续存储,但容量固定且大多情况的插入/删除低效。
std::array 快速访问,原位连续存储,但元素数量固定且不支持插入/删除。
std::deque 快速访问,可在序列首/尾而非中部高效地插入/删除
std::list
std::forward_list
在序列中部可高效地插入/删除,但访问效率大多为线性时间。

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
LWG 139 C++98 在指定的容器上也不需要实现可选操作 需要以均摊常数时间实现
LWG 149 C++98 a.insert(p, t) 返回 iterator,但

a.insert(p, n, t)a.insert(p, n, t) 返回 void

都返回 iterator
LWG 151 C++98 要求 q1 可解引用[1] 它不需要可解引用
LWG 355 C++98 调用 a.back()a.pop_back() 会执行危险[2]的操作 --a.end() 改成自减 a.end() 的副本
LWG 589 C++98 ij 指代的对象不一定可转换到 value_type 这些对象可以隐式转换到 value_type
LWG 3927 C++98 operator[] 没有隐式要求 添加隐式要求
  1. 它是缺陷的原因是它会导致在空容器 a 上调用 a.erase(a.begin(), a.end()) 的行为未定义。
  2. 如果 a.end() 具有基础类型,那么 --a.end() 非良构。当 a 时模板化类型的时候这个操作就变成了危险操作,它会在 a 的类型从非基础类型变成基础类型的时候导致编译失败,并且难以排查。