According to 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?
答案 0 :(得分:9)
根据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的移动构造函数抛出异常,则效果未指定。
所以看起来cppreference.com是错误的。