面对抛出的move构造函数/赋值运算符,std :: vector :: emplace()是否真的提供了强大的异常保证? [英] Does std::vector::emplace() really offer the strong exception guarantee in the face of a throwing move constructor/assignment operator?

查看:88
本文介绍了面对抛出的move构造函数/赋值运算符,std :: vector :: emplace()是否真的提供了强大的异常保证?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据cppreference.com ,std :: vector :: emplace()无条件地提供了强大的异常保证:

According to cppreference.com, std::vector::emplace() offers the strong exception guarantee unconditionally:


如果抛出异常(例如,构造函数),则容器将保持不变,好像从未调用过此函数(强烈的异常保证)。

If an exception is thrown (e.g. by the constructor), the container is left unmodified, as if this function was never called (strong exception guarantee).

但是,在实践中似乎并非如此GCC 7.1.1。以下程序:

However, this doesn't seem to be the case in practice with GCC 7.1.1. The following program:

#include <iostream>
#include <vector>

struct ugly
{
  int i;

  ugly(int i) : i{i} { }

  ugly(const ugly& other) = default;

  ugly& operator=(ugly&& other) {
    if (other.i == 3) {
      throw other.i;
    }
    i = other.i;
    return *this;
  }

  ugly& operator=(const ugly& other) = default;
};

int main() {
  std::vector<ugly> vec;
  vec.reserve(6);
  vec.emplace_back(0);
  vec.emplace_back(1);
  vec.emplace_back(2);
  vec.emplace_back(4);
  vec.emplace_back(5);

  try {
    vec.emplace(vec.begin() + 3, 3);
  } catch (int i) {
  }

  for (const auto& u : vec) {
    std::cout << u.i << "\n";
  }

  return 0;
}

打印


0
1
2
4
4
5


事实上,我很难理解emplace()如何在复制时提供强有力的保证/移动允许抛出。要放置在中间,我们必须先移开一堆元素,然后在其位置构造新元素。如果有任何抛出,我们必须将所有其他元素移回原来的位置,但是这些移动也可能抛出!

In fact, I have a hard time seeing how emplace() could possibly provide the strong guarantee if copying/moving is allowed to throw. To emplace in the middle, we have to move a bunch of elements out of the way first, then construct the new element in its place. If any of that throws, we'd have to move all the other elements back where they were, but those moves can throw too!

所以谁错了,cppreference或gcc ?

So who's wrong, cppreference or gcc?

推荐答案

根据 C ++ 14 标准,强烈例外保证仅在插入的类型本身具有强异常保证的情况下成立。

According to the C++14 standard the strong exception guarantee only holds if the type you insert itself has a strong exception guarantee.

此处:


23.3.6.5 矢量修饰符 [ vector.modifiers ]

23.3.6.5 vector modifiers [ vector.modifiers ]

iterator insert(const_iterator position, const T& x);
iterator insert(const_iterator position, T&& x);
iterator insert(const_iterator position, size_type n, const T& x);
template <class InputIterator>
iterator insert(const_iterator position, InputIterator first, InputIterator last);
iterator insert(const_iterator position, initializer_list<T>);
template <class... Args> void emplace_back(Args&&... args);
template <class... Args> iterator emplace(const_iterator position, Args&&... args);
void push_back(const T& x);
void push_back(T&& x);

1 说明:新的大小大于旧的容量。如果没有发生重新分配,则插入点之前的所有迭代器和引用均保持有效。如果不是由复制构造函数,移动构造函数,赋值运算符或T的移动赋值运算符或任何InputIterator操作抛出异常,则没有任何作用。如果在末尾插入单个元素时抛出异常,并且T为CopyInsertable或is_nothrow_move_constructible :: value为true,则没有效果。否则,如果非CopyInsertable T的move构造函数引发异常,则效果未指定。

1 Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects. If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructible::value is true, there are no effects. Otherwise, if an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.

cppreference.com 错误。

这篇关于面对抛出的move构造函数/赋值运算符,std :: vector :: emplace()是否真的提供了强大的异常保证?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆