首先,假设A
是一个类型:
这是C ++ 03 RAII类型的常见示例。现在让我引用C ++ 14标准(剪掉不相关的部分):
§23.2.1一般容器要求
11 除非另有说明(参见......和23.3.6.5),否则在此定义所有容器类型 条款符合以下附加要求:
- 如果插入单个元素时
insert()
或emplace()
函数抛出异常,则该函数无效。§23.3.6.5
vector
修饰符iterator insert(const_iterator position, const T& x); ...
1 备注:如果新大小大于旧容量,则会导致重新分配。如果没有重新分配,插入点之前的所有迭代器和引用仍然有效。如果除了复制构造函数之外抛出异常,移动构造函数,赋值运算符或移动赋值运算符
T
或任何InputIterator
运算都没有效果。如果在末尾插入单个元素时抛出异常且T
为CopyInsertable
或is_nothrow_move_constructible<T>::value
为true
,则无效。否则,如果非CopyInsertable
T
的移动构造函数抛出异常,则效果未指定。2 复杂性:复杂性是插入元素数量的线性加上到终点的距离 矢量。
现在考虑一下:
std::vector<A> v(5);
v.reserve(10);
v.insert(begin() + 2, A());
显然我们正在插入单个元素,因此§23.2.1 - 11适用,操作成功或v
不变。 §23.3.6.5对此没有任何改变。复制构造函数抛出异常。我们最后没有插入。不使用移动构造函数。
但是现在考虑在插入的实现过程中这种可能的情况,假设没有重新分配:
01234_____ initial state
0123_4____ making space by copying
012_34____ continued
012?34____ continued, but copy operation threw
此时所有未来的复制操作都可能抛出,因此无法根据需要恢复状态。糟糕。
如果没有重新分配可以实现强大的异常安全性,我看不到任何实现。这意味着在插入没有移动构造函数和中间抛出复制构造函数的类型时,任何实现都必须始终重新分配。但是:
insert(pos, value)
变得无法忍受。n
次操作)。可以认为“如果新大小大于旧容量,则会导致重新分配。”意味着如果新大小不大于旧容量,则不允许重新分配
为了支持这一点,请考虑如果实现可能随时重新分配,则用户无法知道。这保证了保留迭代器(“如果没有重新分配,插入点之前的所有迭代器和引用仍然有效。”)无用的信息,并且让你想知道为什么两个句子都被插入到标准中首先。
1&amp; 2是非常诅咒的观察,但如果3是真的那么它(据我所见)根本不可能符合标准。
那么,有没有办法为符合标准的向量实现insert方法?或者这是标准缺陷吗?
此处可以看到此问题的演示:http://coliru.stacked-crooked.com/a/afd2e838c34c8fcc
答案 0 :(得分:8)
就我对标准的解释而言,这里“除非另有说明”,否则一旦在特定容器的相应条款中为insert
指定了有关异常的任何内容,则§中的列表的要点23.2.1不再适用。
如果除了复制构造函数之外引发了异常 [...]
T
[..]没有效果。
表示相反:当T
的复制构造函数抛出异常时,无法保证该调用不会产生任何影响。
如果插入单个元素时
insert()
或emplace()
函数抛出异常,则该函数无效
不适用:§23.3.6.5指定“否则”。