Действительно ли std::vector::emplace() предлагает гарантию сильных исключений перед оператором / оператором присваивания броска?

Согласно cppreference.com, std:: vector:: emplace() предлагает безусловную гарантию исключений:

Если выдается исключение (например, конструктором), контейнер остается неизменным, как если бы эта функция никогда не вызывалась (гарантия сильного исключения).

Однако на практике это не похоже на GCC 7.1.1. Следующая программа:

#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() может предоставить надежную гарантию, если копирование / перемещение разрешено. Чтобы разместить в середине, мы должны сначала убрать группу элементов, а затем создать новый элемент на его месте. Если что-то из этого броска, мы должны будем переместить все остальные элементы туда, где они были, но эти движения тоже могут бросить!

Так кто же не прав, cppreference или gcc?

1 ответ

Решение

Согласно C++14 Стандартная гарантия строгого исключения действует только в том случае, если тип, который вы вставляете сам, имеет гарантию строгого исключения.

Вот:

23.3.6.5. Векторные модификаторы [ 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, эффекты не определены.

Похоже, что http://en.cppreference.com/w/ не так.

Другие вопросы по тегам