fill insert() - 复制构造函数和复制赋值 noexcept 状态?

fill insert() - copy constructor and copy assignment noexcept status?

  1. STL 容器元素是否需要 noexcept 复制构造函数和复制赋值运算符?如果可能,请提供参考。
  2. 如果不是,当多次插入期间发生异常时,STL 容器的状态是什么,例如填充期间插入.

在尝试编写允许 intercepting/vetoing 修改的通用包装器时出现问题。我能想到的任何实现都可能会改变底层容器的语义,除非专门针对每种容器类型(这实际上不是一个选项)。

例如,std::vector有一个填充插入

void insert (iterator position, size_type n, const value_type& val);

这要求 value_type 既是 CopyInsertable 又是 CopyAssignable。请注意,它 要求值类型为 DefaultConstructible.

编辑3 Stroustrup自己(table第956页)表示多元素插入是应该对所有 vector、deque、list 和 map 有 strong guarantee。这意味着完整的标准库操作要么成功要么失败。

编辑4 但是,只有当相关操作(在本例中是复制构造函数)本身不抛出异常时,保证才适用,这正是我的问题。

据我了解,这就剩下两个基本的实现方法:

  1. 为新元素创建 虚拟 条目并复制分配 val。这仅在可以通过复制容器中的现有元素或 value_typeDefaultConstructible(这不是必需的)来创建虚拟元素时才有效。
  2. 将构造元素一个接一个地直接复制到它们在容器中的相应位置。这似乎或多或少是规范的实现。

编辑 2:我不会称其为 未定义行为,因为该术语似乎会提醒人们思考 未定义为语言 runtime/the 标准.

这两种实现似乎都给容器留下了未知内容(即不清楚异常后容器包含哪些元素)当复制构造函数或者复制赋值运算符引发异常.

编辑 1:请注意,这并不意味着我假设 C++ 运行时存在不良行为,例如内存泄漏或未定义的值。然而,容器的内容似乎或多或少是未指定。特别是,容器的内容可能已被完全(尽管是一致的)改变。

例如,考虑第三种(混合)方法:

  1. 创建模板对象 n 个副本的列表 val
  2. 将此列表中的元素复制并分配到目标容器中。

不同之处在于复制构造函数引发异常时对容器的影响。在这种情况下,如果复制构造函数抛出,容器的内容保持不变(但当复制赋值运算符抛出时,仍会导致未指定的内容)。当使用指针时(即当不使用 std::vector 时),复制赋值可能会被省略,只有指针重新排列,使操作成为原子操作。例外。

至于容器元素上的 noexcept:对象是通过 allocator_traits<value_type>::construct(ptr, args) 创建的,而不是 noexcept 而且我也找不到容器元素大多数具有 [= 的要求11=] 复制 constructor/copy-assigment 运算符(例如 std::shared_ptrstd::unique_ptr 需要这个)。

请注意,自动生成的复制构造和复制分配操作必须是 noexcept,除非它们需要调用本身可能引发异常的操作。

这让我很困惑,我确定我错过了什么,但我无法弄清楚标准中可能证明我错了的部分。

  1. Are STL container elements required to have noexcept copy-constructors and copy-assignment operators? Please provide a reference if possible.

不,它们不是,我无法为不存在的需求提供参考,因为它不存在。

如果有要求,标准中会这样说,但事实并非如此。

一般情况下,元素根本不需要复制构造函数,移动构造函数对大多数操作来说就足够了。复制分配也是如此。

  1. If not, what is the state of a STL container when an exception happens during a multi-insert, e.g. during fill insert.

这取决于容器,以及您在容器中插入的位置。

对于基于节点的容器(如列表),如果一次插入抛出异常,则任何已插入的元素都会保留在容器中。

对于 std::vector 究竟会发生什么取决于您在向量中插入的位置以及是否需要重新分配,以及元素是否具有 noexcept 移动构造函数。你所能依赖的就是没有泄漏,也没有部分构造的元素,所以向量处于有效状态。

Both implementations seem to leave the container in an undefined state (i.e. it is not clear what elements the container holds after the exception) when either the copy constructor or the copy-assignment operator raise an exception.

这不是一个未定义的状态,它只是一个您没有完整信息的状态。您可以使用 vector::size()vector::capacity() 来确定其状态,并检查位置 [0, size()) 的元素以检查元素的状态。之后你就知道了向量状态的一切。

即矢量始终处于有效状态,您只是不知道足够的信息来准确描述该状态,直到您检查它。

"undefined" 这个词暗示状态不一致,或者不可知,这是不正确的。标准容器总是给出 basic exception-safety guarantee,因此失败的操作不会使它们处于未定义状态。

即使在vector::push_back()这样的极端情况下,元素类型不可复制且具有抛出移动构造函数,异常也会使向量处于"unspecified"状态,因此没有泄漏,没有无效对象,也没有未定义的行为。

As an example, consider a third (hybrid) method:

  • create a list of n copies of the template object val.
  • copy-assign the elements from this list into the target container.

The difference is the effect on the container when the copy constructor raises an exception. In this case, the contents of the container remain unchanged if the copy-assignment operator throws.

也许我误会了,但我看不出这比 "Copy-construct elements one after the other directly into their respective locations in the container." 有什么更好的。就容器的最终状态而言,我看不到任何优势。

在这两种情况下,您都需要向容器添加 n 个新元素,这可能会抛出,如果它抛出,我不明白为什么它对最终状态有任何影响,无论您是否也在计划之后做一些额外的作业。