C++ 命名需求: AllocatorAwareContainer (自 C++11 起)
来自 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()
获取用于构造容器或由最近的分配器替换操作安装的分配器的副本。
内容 |
[编辑] 要求
如果类型满足 Container,并且给定以下类型和值,则该类型满足 AllocatorAwareContainer,并且下面表格中的语义和复杂度要求得到满足
类型 | 定义 |
X
|
一个 AllocatorAwareContainer 类型 |
T
|
X 的 value_type |
A
|
X 使用的分配器类型 |
值 | 定义 |
a, b | X 类型的非 const 左值 |
c | 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 可以 移动插入 到 X 中。 |
|
后置条件 |
|
[编辑] 表达式
表达式 | 类型 | 语义 | 复杂度 | |
---|---|---|---|---|
c.get_allocator() | A
|
没有直接的语义要求。 | 常数 | |
a = t | X&
|
前提条件 | T 可以 复制插入 到 X 中,并且可以 复制赋值。 |
线性 |
后置条件 | a == t 为 true。 | |||
a = rv | X&
|
前提条件 | 如果分配器不会被移动赋值替换(参见 以上),则 T 可以 移动插入 到 X 中,并且可以 移动赋值。 |
线性 |
效果 | a 中所有现有的元素都将被移动赋值或销毁。 | |||
后置条件 | 如果 a 和 rv 不引用同一个对象,则 a 等于赋值之前 rv 的值。 | |||
a.swap(b) | void | 效果 | 交换 a 和 b 的内容。 | 常数 |
[编辑] 备注
分配器感知容器总是调用 std::allocator_traits<A>::construct(m, p, args) 来使用 args 在 p 处构造一个 T
类型的对象,其中 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++ 标准。
DR | 应用于 | 发布的行为 | 正确的行为 |
---|---|---|---|
LWG 2839 | C++11 | 不允许标准容器进行自我移动赋值 | 允许,但结果是不确定的 |