C++ 具名要求:知分配器容器 (AllocatorAwareContainer)
来自cppreference.com
知分配器容器 (AllocatorAwareContainer) 是容器 (Container) ,其保有一个分配器 (Allocator) 实例,并于其所有成员函数中用该实例来分配及解分配内存,并于这个内存中构造及销毁对象的(这种对象可以是容器元素,结点,或对于无序容器为桶数组),除了 std::basic_string 特化不用分配器构造/析构其元素 (C++23 起)。
下列规则适用于容器的构造
- 知分配器容器 (AllocatorAwareContainer) 的复制构造函数,通过在正在复制的容器的分配器上调用 std::allocator_traits<allocator_type>::select_on_container_copy_construction 获得自己的分配器实例。
- 移动构造函数通过从属于旧容器的分配器进行移动构造,获得其自己的分配器实例。
- 所有其他构造函数均接收一个 const allocator_type& 形参。
仅有的替换分配器的方式是进行移动赋值、复制赋值及交换:
- 仅当 std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value 为 true 时,复制赋值才会替换分配器
- 仅当 std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value 为 true 时,移动赋值才会替换分配器
- 仅当 std::allocator_traits<allocator_type>::propagate_on_container_swap::value 为 true 时,交换才会替换分配器。特别是它将通过对非成员函数 swap 的无限定的调用来交换分配器实例,见可交换 (Swappable) 。
注:若 propagate_on_container_swap
为 false,则交换两个拥有不相等分配器的容器是未定义行为。
- 访问器
get_allocator()
获得构造容器时所用的,或为最近的分配器替换操作所安装的分配器的一个副本。
要求
凡例
X
|
容器类型 |
T
|
元素类型 |
A
|
T 的分配器
|
a , b
|
X 类型的对象(非 const 左值)
|
t
|
X 类型的对象(左值或 const 右值)
|
rv
|
X 类型的对象(非 const 右值)
|
m
|
A 类型的对象
|
Q
|
分配器类型 |
表达式 | 返回类型 | 前提/要求 | 后置/效应 | 复杂度 |
---|---|---|---|---|
allocator_type | A |
allocator_type::value_type 必须与 X::value_type 相同 | 编译时 | |
get_allocator() | A |
常数 | ||
X u; | A 可默认构造 (DefaultConstructible) |
u.empty() == true && u.get_allocator() == A() | 常数 | |
X u(m); | u.empty() == true && u.get_allocator() == m | 常数 | ||
X u(t,m); | T 可复制插入 (CopyInsertable) 到 X
|
u == t && u.get_allocator() == m | 线性 | |
X u(rv); | A 的移动构造函数必须不抛异常
|
u 拥有与 rv 构造前相同的元素和相等的分配器 |
常数 | |
X u(rv,m); | T 可移动插入 (MoveInsertable) 到 X
|
u 的元素与 rv 的相同或是其副本,且 u.get_allocator() == m
|
若 m == rv.get_allocator() 则为常数,否则为线性 | |
a = t | X& | T 可复制插入 (CopyInsertable) 到 X 且可复制赋值 (CopyAssignable) |
a == t | 线性 |
a = rv | X& | 若分配器将不被移动赋值所替换(见上文),则 T 可移动插入 (MoveInsertable) 到 X 并可移动赋值 (MoveAssignable)
|
a 的所有既存元素被移动赋值或销毁;若 a 与 rv 不指代同一对象则 a 等于 rv 赋值前所拥有的值 |
线性 |
a.swap(b) | void | 交换 a 与 b 的内容 |
常数 |
注解
具分配器容器始终调用 std::allocator_traits<A>::construct(m, p, args) 在 p
中用 args
来构造一个 A
类型的对象,其中 m == get_allocator()。std::allocator 中默认的 construct
调用 ::new((void*)p) T(args) (C++20 前)std::allocator 无 construct
成员,而在构造元素时 std::construct_at(p, args) 得到调用 (C++20 起),但特化的分配器可以选择不同的定义。
标准库
所有标准库容器,除了 std::array,都是知分配器容器 (AllocatorAwareContainer) :
- std::basic_string
- std::deque
- std::forward_list
- std::list
- std::vector
- std::map
- std::multimap
- std::set
- std::multiset
- std::unordered_map
- std::unordered_multimap
- std::unordered_set
- std::unordered_multiset
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
LWG 2839 | C++11 | 不允许标准容器的自移动赋值 | 容许但结果未指定 |