C++ 具名要求:知分配器容器 (AllocatorAwareContainer) (C++11 起)
知分配器容器 (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()
获得构造容器时所用的,或为最近的分配器替换操作所安装的分配器的一个副本。
要求
一个类型满足知分配器容器 (AllocatorAwareContainer) 的条件是,它满足容器 (Container) ,并且给定以下类型和值,它也满足下方表格列出的语义和复杂度要求:
类型 | 定义 |
X
|
知分配器容器 (AllocatorAwareContainer) 类型 |
T
|
X 的 value_type
|
A
|
X 使用的分配器类型
|
值 | 定义 |
a, b | X 类型非 const 左值
|
c | const X 类型左值 |
t | X 左值或 const 右值
|
rv | X 类型非 const 右值
|
m | A 类型的值
|
类型
名字 | 类型 | 要求 |
---|---|---|
typename X::allocator_type | A
|
X::allocator_type::value_type 与 X::value_type 相同。
|
语句
语句 | 语义 | 复杂度 | |
---|---|---|---|
X u; X u = X(); |
前条件 | A 可默认构造 (DefaultConstructible) 。
|
常数 |
后条件 | u.empty() 和 u.get_allocator() == A() 都是 true。 | ||
X u(m); | 后条件 | u.empty() 和 u.get_allocator() == m 都是 true。 | 常数 |
X u(t, m); | 前条件 | T 可复制插入 (CopyInsertable) 到 X 中。
|
线性 |
后条件 | u == t 和 u.get_allocator() == m 都是 true。 | ||
X u(rv); | 后条件 |
|
常数 |
X u(rv, m); | 前条件 | T 可移动插入 (MoveInsertable) 到 X 中。
|
|
后条件 |
|
表达式
表达式 | 类型 | 语义 | 复杂度 | |
---|---|---|---|---|
c.get_allocator() | A
|
没有直接语义要求。 | 常数 | |
a = t | X&
|
前条件 | T 可复制插入 (CopyInsertable) 到 X 中,并且可复制赋值 (CopyAssignable) 。
|
线性 |
后条件 | a == t 是 true。 | |||
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 和 std::inplace_vector)都是知分配器容器 (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 | 不允许标准容器的自移动赋值 | 容许但结果未指定 |