【发布时间】:2013-08-03 11:42:57
【问题描述】:
我对异常安全和 STL 容器/迭代器有疑问。
出于某种原因,我假设简单容器的迭代器
std::vector<POD Type>
只要您停留在区间 [begin(), end()) 内,在对其执行算术运算(或取消引用它)时不会引发异常。我试图在标准中查找(使用 N3337),但我发现没有给出这样的 nothrow 保证(但也许我错过了一些东西!)。另见:May STL iterator methods throw an exception
到现在为止,我写了很多通常会被破坏的代码,考虑到即使对于具有合理元素类型的简单容器也没有说 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_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() 迭代器不引用任何元素,所以它可能是 无效。 ——尾注]
【问题讨论】:
-
不支持在迭代容器时调用 container.erase()。如果您这样做,实现可能会简单地崩溃或抛出异常。
-
brian beuning:我不确定你在说什么——这跟我的问题有关吗?不过,在迭代像 STD 序列容器这样的容器时调用擦除是完全有效的!
-
@brianbeuning std::vector
v; /* ... 填充 v 使其不为空。 */ for(auto i = v.begin(); i!=v.end(); i=v.erase(i));断言(v.empty());但这与主题无关! -
有 17.6.5.12“异常处理限制”,它几乎说 C++ 标准库中的函数只能抛出函数描述的 Throws: 部分中指定的内容。除了 17.6.4.8p2 “对 [用作模板参数的类型] 的操作可以通过抛出异常来报告失败,除非另有说明。”因此,除非 a) 明确允许,或 b) 从用户定义的类型传播且未明确禁止,否则不应引发异常。这是否是一个足够的保证?
标签: c++ exception stl iterator