C++迭代程序异常安全

C++ Iterator Exception Safety

本文关键字:安全 异常 程序 迭代 C++      更新时间:2023-10-16

我在异常安全和STL容器/迭代器方面遇到问题。

出于某种原因,我假设简单容器的迭代器

std::vector<POD Type> 

在对它(或deref.it)执行算术运算时,只要你保持在区间[begin(),end())内,就不会抛出异常。我试图在标准中查找它(使用N3337),但我发现没有给出这样的nothrow保证(但可能我错过了什么!)。另请参阅:可能STL迭代器方法抛出异常

到目前为止,我写了一些通常会被破坏的代码,考虑到即使对于具有合理元素类型的简单容器,也没有所谓的nothrow保证。

例如,以下内容可能仍然引发异常(其中c是std::vector实例):

for(... i = c.begin(); i != c.end(); ++i) { /* do something here - guaranteed to not throw. */ }

但这会在不同的STD库中引发异常安全和程序稳定性问题,因为据我所知,您必须了解迭代器操作的实现。

例如,以Boost.Graph的邻接列表的clear()函数为例(Boost中还有更多这样的例子),并假设容器m_顶点是一个类似std::vector的std序列容器。

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
}

应该保证此函数不会抛出,因为它是在相邻列表的析构函数<…>中调用的假设没有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()或template()函数在插入单个元素时引发异常,则函数没有任何效果。

--如果pushback()或pushfront()函数抛出异常,则该函数没有任何效果。

--没有erase()、clear()、pop_back()或pop_front()函数抛出异常。

--返回迭代器的复制构造函数或赋值运算符都不会引发异常。

--没有swap()函数抛出异常。

--没有swap()函数使引用正在交换的容器。

[注意:end()迭代器没有引用任何元素,因此它可能是无效--结束注释]

只有宽合约(即不可能失败的操作)才没有抛出保证。所有迭代器操作都有狭窄的约定(即,它们有一些前提条件),因此,当不满足前提条件时,可能会以任意方式失败。因此,它们没有任何异常保证,因为未定义的行为前提条件不满足可能会导致给定的实现抛出异常。假设先决条件得到满足,并且行为不包括抛出任何异常,则各个迭代器操作的行为都得到了很好的定义:迭代器运算的行为在需求表中定义。

也就是说,总的来说,你应该期望所有的操作都有可能首先抛出。为了从异常中进行正确的恢复:然而,有时有必要知道特定的函数不会抛出,否则恢复可能会失败,某些相当基本的操作(如交换两个内置类型的对象)被定义为不抛出。