c++ - std::vector::push_back 的成本是成功还是无效?

标签 c++ c++11 vector stl

如果我理解正确,std::vector::insert不保证 std::vector 的提交或回滚s(出于明显的原因,它适用于 std::lists)在复制或移动期间抛出异常的情况下,因为检查异常的成本很高。我最近看到 push_back 确实可以保证最后插入成功或什么也没有发生。

我的问题如下。假设在 push_pack 期间必须调整 vector 的大小(发生重新分配)。在这种情况下,必须通过复制或移动语义将所有元素复制到新容器中。假设移动构造函数不能保证 noexcept,那么 std::vector 将使用复制语义。因此,为了保证 push_pack 的上述行为,std::vector 必须检查复制是否成功,如果没有,则通过交换初始 vector 回滚。这是正在发生的事情吗?如果是这样,这不是很贵吗?或者,因为很少发生再分配,可以说摊销成本很低?

最佳答案

在 C++98/03 中,我们(显然)没有移动语义,只有复制语义。而在 C++98/03 中,push_back有强有力的保证。 C++11 的主要动机之一是不破坏依赖这种强保证的现有代码。

C++11 规则是:

  1. 如果 is_nothrow_move_constructible<T>::value为真,移动,否则
  2. 如果 is_copy_constructible<T>::value为真,复制,否则
  3. 如果 is_move_constructible<T>::value为真,移动,否则
  4. 代码格式不正确。

如果我们在 1 或 2 的情况下,我们有 strong guarantee .如果我们在案例 3 中,我们只有 basic guarantee .由于在 C++98/03 中,我们总是在情况 2 中,因此我们具有向后兼容性。

案例2不贵,维护强保。分配新缓冲区,最好使用 RAII 设备,例如第二个 vector 。复制到其中,只有当所有这些都成功时,交换 *this与 RAII 设备。这是最便宜的做事方式,无论您是否想要强有力的保证,而且您都可以免费获得。

案例 1 也很便宜,可以保持强大的保证。我知道的最好方法是首先将新元素复制/移动到新分配的中间。如果成功,则将元素从旧缓冲区中移出并交换。

比您可能想要的更多细节

libc++使用相同的算法完成所有 3 种情况。为此,使用了两个工具:

  1. std::move_if_noexcept
  2. 非标准 vector - 类似容器,其中数据是连续的,但可以从分配的缓冲区开头的非零偏移量开始。 libc++ 调用这个东西 split_buffer .

假设重新分配情况(非重新分配情况很简单),split_buffer是参照这个 vector 构造的的分配器,容量是 vector 的两倍, 并将其起始位置设置为 this->size() (虽然 split_buffer 仍然是 empty() )。

然后将新元素复制或移动(取决于我们正在谈论的 push_back 重载)到 split_buffer .如果失败,split_buffer析构函数撤消分配。如果成功,则 split_buffer现在有size() == 1 ,以及 split_buffer正好有 this->size() 的空间第一个元素之前的元素。

接下来元素从this 以相反的顺序移动/复制split_buffer . move_if_noexcept 用于此,返回类型为 T const&T&&完全符合上述 3 种情况的要求。在每次成功移动/复制时,split_buffer正在做 push_front .如果成功,split_buffer现在有size() == this->size()+1 , 并且它的第一个元素是从它分配的缓冲区开始的零偏移量。如果任何移动/复制失败,split_buffer的析构函数会破坏 split_buffer 中的任何内容并释放缓冲区。

接下来是split_bufferthis交换他们的数据缓冲区。这是 noexcept操作。

最后是 split_buffer destructs,销毁其所有元素,并释放其数据缓冲区。

无需 try catch 。没有额外的费用。一切都按照 C++11 的规定运行(并在上面总结)。

关于c++ - std::vector::push_back 的成本是成功还是无效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23859112/

相关文章:

c++ - 空 vector 是其结构的大小吗?

c++ - 在 C++ 中使用 pthreads 实现线程池

c++ - 如果我得到任何字符,则退出程序

c++ - 使用可变参数包实例化函数模板

vector - 矢量 (0,0,0) 朝向什么方向?

c++ - 从不与对象大小对齐的连续固定大小缓冲区解析对象的有效方法

c++ - OpenCV 比较两幅图像并得到不同的像素

c++ - 如何以编程方式克隆 mysql 数据库

c++ - 如何在编译时切换/选择类型?

C++11 async 只使用一个核心