为什么 std::vector 的交换函数与所有其他容器的交换函数具有不同的 noexcept 规范?

问题描述 投票:0回答:1

我注意到

std::vector
容器的交换函数具有与所有其他容器不同的 noexcept 规范。具体来说,如果表达式
std::allocator_traits<Allocator>::propagate_on_container_swap || std::allocator_traits<Allocator>::is_always_equal
为 true,但其他容器要求表达式
std::allocator_traits<Allocator>::is_always_equal
为 true。

既然交换函数的行为是相同的,为什么仅在

std::vector
容器中的 noexcept 规范不同?

c++ c++17 stdvector swap noexcept
1个回答
0
投票

您问题中讨论的

noexcept
规范是通过C++17通过N4258引入的。本文从
vector
/
string
与其他容器(
deque
list
map
等)的区别在于
noexcept
移动分配1规范和
swap
功能,总结下面2

集装箱 移动作业
swap
vector
/
string
POCMA || IAE
POCS || IAE
其他
IAE
IAE

对于

vector
string
以外的容器,仅依靠
POCMA
来保证移动分配的
noexcept
是不够的。某些实现需要使用移动的分配器分配哨兵以实现移出状态,这可能会引发异常。值得注意的是,微软的
std::set
实现有一个投掷移动赋值运算符。论文解释:

[...] 即使使用 POCMA,我们也不能保证

noexcept
容器是否是基于“某种”节点的(双端队列、列表、关联、无序):如果分配器传播并且移出的对象需要重新分配,并不总是相等(因为我不能偷 LHS 的记忆)。

现在,关于

swap
,上述推理不适用,因为我们只需要交换所有内容,包括提到的哨兵对象。因此,不需要重新分配,
POCS || IAE
就足够了。 P0177R2中提供了类似的原理:

[...] 不过,这对于交换操作来说不应该是问题,因为预计会交换分配器以及满足不变量的两个数据结构。 [...]

讨论了这些要点后,当前标准似乎可能存在缺陷。一个可能的原因可能是委员会的意图是允许基于节点的容器的

swap
操作根据其移动分配来实现,从而导致与后者相同的
noexcept
规范。不幸的是,N4258 中提到的可能提供进一步见解的讨论是非公开的(来自WG21 Wiki),因此这仍然是推测性的。

为了获得更多见解,我调查了三个主要供应商基于节点的容器的实现,以确定他们为

noexcept
功能提供的
swap
保证。以下是调查结果3

集装箱 libc++ libstdc++ 微软STL
deque
noexcept
noexcept
noexcept
list
noexcept
noexcept
noexcept
forward_list
noexcept
noexcept
noexcept
set
noexcept
noexcept
noexcept
unordered_set
noexcept
noexcept
noexcept

如表中所示,所有这些实现确实为其各自的

noexcept
功能提供了强化的
swap
规范。


1虽然问题中没有直接解决移动分配,但这里提到它是因为它可能会影响

noexcept
操作的
swap
规范的设计。
2
POCMA
代表
propagate_on_container_move_assignment
POCS
代表
propagate_on_container_swap
IAE
代表
is_always_equal
。为了简洁起见,此处省略了诸如
Compare
规范之类的谓词。

3与之前的脚注类似,为简洁起见,谓词规范已被省略。

© www.soinside.com 2019 - 2024. All rights reserved.