我试图理解以下 C++ 代码的异常安全性:
#include <vector>
#include <array>
int main() {
std::vector<int> v;
std::array<int, 500> b{};
v.assign(b.begin(), b.end());
}
在此代码中,我将元素从
std::array
分配给 std::vector
。我查看了 std::vector::assign
的 cppreference 文档,但找不到有关在此操作期间可以抛出哪些异常或提供哪些异常保证的具体细节。
我的担忧:
内存分配:我知道如果
std::vector
需要增加大小,它可能必须分配新的内存。这可能会引发 std::bad_alloc
异常。
部分赋值:如果将
std::array
中的某些元素复制到 std::vector
后发生异常,看起来 a 可能会处于不一致的状态,这违反了强大的异常安全保证。但在这种情况下 int
是不可复制的,因为它是普通类型。
根据我的理解,有人可以告诉我
a.assign(b.begin(), b.end())
行是否被认为是异常安全的?
我什至尝试研究标准库的实现之一来检查它是否是,但我无法确定。
起初我以为我们可以将其分解为两部分,一个用于分配,一个用于分配部分,通过在调用
reserve
之前调用 assign
:
#include <vector>
#include <array>
int main() {
std::vector<int> v;
std::array<int, 500> b{};
v.reserve(b.size());
v.assign(b.begin(), b.end());
}
我的希望是能够说:“我们都同意
reserve
调用可以抛出异常,这样就只剩下赋值部分了。
不幸的是,我认为这不起作用。 [sequence.reqmts]/57-59 中的披露清楚地表明,期望
a.assign(b.begin(), b.end());
大致相当于:
std::vector<int> temp(b.begin(), b.en());
a = temp;
...所以它总是分配空间,即使它已经有能力容纳分配的范围。
不幸的是,[sequence.reqmts] 没有 Throws 子句,[vector.overview] 也没有。
在 [res.on.exception.handling]/4 中只剩下极其笼统的描述:
C++ 标准库中定义的函数没有 Throws: 段落,但确实有一个潜在的- 抛出异常规范可能会抛出实现定义的异常。168 实现应该 通过抛出标准异常类或从标准异常类派生的异常来报告错误(17.6.4.1、17.9、19.2)。
- 特别是,他们可以通过抛出 bad_alloc 类型的异常或派生类来报告分配存储失败 来自 bad_alloc (17.6.4.1)。
所以,官方的答案是标准中没有任何内容可以阻止它抛出异常。
我认为您已经发现,从实际角度来看,很难想象一种将
int
从一个内存块复制到另一个内存块会抛出异常的方法 - 但恐怕标准中没有任何内容详细介绍了这种情况,说它只能在分配失败时抛出异常(尽管脚注 168 确实暗示了这种情况会抛出异常)。
我认为你得到的答案相当不令人满意,即如果分配成功,复制时几乎不可能抛出异常——但标准中似乎没有任何内容可以真正保证这一点。