我对异常安全和 STL 容器/迭代器有疑问。
出于某种原因,我假设简单容器的迭代器
std::vector<POD Type>
在对其执行算术运算(或取消引用)时不会抛出异常,只要您保持在区间 [begin(), end()) 内即可。我试图在标准中查找(使用 N3337),但我发现没有给出这样的 nothrow 保证(但也许我错过了什么!)。另见:May STL iterator methods throw an exception
到目前为止,我写了很多通常会被破坏的代码,考虑到即使对于具有合理元素类型的简单容器也没有所谓的不抛出保证。
例如像下面这样的东西可能仍然会抛出异常(其中 c 是一个 std::vector 实例):
for(... i = c.begin(); i != c.end(); ++i) { /* do something here - guaranteed to not throw. */ }
但这会导致跨不同 STD 库的异常安全和程序稳定性问题,因为据我所知,您必须了解迭代器操作的实现。
例如,使用 Boost.Graph 的邻接表的 clear() 函数(Boost 中还有更多这样的例子)并假设容器 m_vertices 是一个标准序列容器,如 std::vector。
inline void clear() {
for (typename StoredVertexList::iterator i = m_vertices.begin(); // begin() and copy assignement does not throw (according to the STD)
i != m_vertices.end(); ++i) // ++i and operator != () might throw
delete (stored_vertex*)*i; // *i might throw
m_vertices.clear(); // will not throw (nothrow per Definition of the STD)
m_edges.clear(); // same
}
这个函数应该保证不会抛出,因为它是在 adjacency_list<...> 的析构函数中调用的,并且假设没有 clear() 函数抛出是合理的,即使我没有找到任何异常安全保证在 Boost.Graph 的文档中。
我希望你能阐明这个异常安全问题,并告诉我我在这里缺少什么。特别是对于什么样的迭代器算术运算和取消引用确实不会抛出以及定义此类保证的地方。
谢谢!
来自 C++ STD 论文 N3337
23.2.1:10)
除非另有说明(参见 23.2.4.1、23.2.5.1、23.3.3.4 和 23.3.6.5),否则本文档中定义的所有容器类型 条款满足以下附加要求:
——如果在插入单个元素时 insert() 或 emplace() 函数抛出异常,则 函数没有任何影响。
— 如果 push_back() 或 push_front() 函数抛出异常,则该函数无效。
— 没有 erase()、clear()、pop_back() 或 pop_front() 函数抛出异常。
— 返回的迭代器的复制构造函数或赋值运算符不会引发异常。
— 没有 swap() 函数抛出异常。
——没有 swap() 函数会使引用元素的任何引用、指针或迭代器无效 正在交换的容器。
[ 注意:end() 迭代器不引用任何元素,因此它可能是 无效。 ——尾注]
最佳答案
只有宽契约(即不可能失败的操作)没有抛出保证。所有迭代器操作都有窄契约(即,它们有一些前提条件),因此,当不满足前提条件时,可能会以任意方式失败。因此,它们没有任何异常保证,因为不满足未定义的行为先决条件可能导致给定的实现抛出异常。假设满足先决条件并且行为不包括抛出任何异常,则各个迭代器操作的行为被明确定义:迭代器操作的行为在需求表中定义。
也就是说,一般来说,您应该预料到所有操作都可能首先抛出异常。要从异常中正确恢复:但是,有时需要知道特定函数不会抛出异常,否则恢复可能会失败,某些相当基本的操作(如交换两个内置类型的对象)被定义为不抛出异常。
关于C++ 迭代器异常安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18032488/