2013-08-03 64 views
1

我有异常安全和STL容器/迭代器的问题。C++迭代器异常安全

我承担了一些原因,简单的容器的迭代器上执行算术运算时

std::vector<POD Type> 

不抛出异常(或DEREF。它)只要你留在区间[开始(), 结束())。我试图用标准来看(使用N3337),但是我发现没有给出这样的保证(但也许我错过了一些东西!)。另请参阅: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. */ } 

但这招在不同的性病库,因为你有例外安全和程序的稳定性问题据我所知,了解迭代器操作的实现。

例如,使用Boost.Graph的邻接列表的clear()函数(并且Boost中还有更多这样的例子),假设容器m_vertices是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 
} 

此功能应保证不丢,因为这就是所谓的adjacency_list <的析构函数...>,这将是合理的假设,没有明确的()函数抛出,即使我没有找到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()函数引发异常,则该函数不起作用。 (),clear(),pop_back()或pop_front()函数抛出异常。

- 返回的迭代器的拷贝构造函数或赋值操作符抛出异常。

- no swap()函数抛出异常。

- 没有swap()函数使任何引用,指针或迭代器都指向正在交换的容器的元素。

[注意:end()迭代器不引用任何元素,因此可能会使 无效。 - 注意]

+0

不支持在迭代容器时调用container.erase()。如果你这样做,一个实现可能会简单地崩溃或者可能抛出一个异常。 –

+0

brian beuning:我不确定你在说什么 - 这与我的问题有关吗?但是在迭代像STD序列容器之类的容器时调用擦除操作完全有效! – livingissuicide

+0

查看http://stackoverflow.com/questions/6438086/iterator-invalidation-rules –

回答

2

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

这就是说,一般来说,你应该期待所有的操作可能会放在第一位。要从异常中进行适当的恢复:但是,有时需要知道特定函数不会抛出,否则恢复可能会失败,因此某些基本操作(如交换内置类型的两个对象)被定义为不抛出。

+0

看起来像我错过了C++标准的基本点。但是,这需要我相当长的一段时间才能弄清楚。谢谢你,先生,这个出色的答案! – livingissuicide

+0

对于大多数迭代器([counterexample](http://stackoverflow.com/a/7903037/3919155))的Afaik唯一(可疑的)良好的用于这种未定义的行为所造成的前提条件不满足抛出的异常将是一些用于调试目的的顶级代码。我认为这可能会导致性能下降,但以稍微好一点的调试为代价。我希望标准库开发人员无论如何都允许为迭代器使用'noexcept'。 – jotik