Действительно ли 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/ не так.