可能std::向量中的元素具有抛出的析构函数

May the elements in a std::vector have a throwing destructor?

本文关键字:析构函数 元素 向量 可能 std      更新时间:2023-10-16

当我查看cppreference上的Container需求时,它将Destructible列为value_type的需求。这似乎意味着容器元素的析构函数可能不会抛出。

我在C++14标准中找不到这一要求的引文(没有在旧版本中找到)。我唯一能找到的是value_type必须是Erasable,这并不意味着任何异常安全性。

所以我的问题是,std::vector中的元素可能有抛出析构函数吗?如果没有,标准中的哪一节禁止?


附言:别担心,我不打算创建带有抛出析构函数的类型。我只是在写一个符合标准的实现,并试图获得正确的异常安全性。

N4140[res.on.functions]/2状态:

特别是,在以下情况下,影响是不明确的:

(2.1)——对于替换功能(18.6.1),如果安装的替换功能没有实现适用的所需行为的语义:段落。

(2.2)-对于处理程序函数(18.6.2.3、18.8.3.1、D.11.1),如果安装的处理程序函数没有实现适用的所需行为的语义:段

(2.3)——对于在实例化模板组件时用作模板参数的类型,如果对该类型的操作没有实现适用的"要求"子条款(17.6.3.5、23.2、24.2、26.2)的语义。除非另有规定,否则对此类类型的操作可以通过抛出异常来报告失败。

(2.4)--如果任何替换函数、处理程序函数或析构函数操作通过例外,除非在适用的"必需行为":第段中特别允许

(2.5)-如果在实例化模板组件时使用不完整类型(3.9)作为模板参数,除非该组件特别允许。

这有点晦涩,但节省了大量空间,否则这些空间将浪费在整个库子句中的"T必须满足可破坏的要求"语句上。

值得注意的是,这并不意味着std::vector的元素不能具有抛出析构函数;这只意味着当从标准库调用时,所述析构函数决不能抛出。因此,例如,该程序符合:

#include <vector>
struct A {
  bool throw_an_int = false;
  ~A() noexcept(false) {
    if (throw_an_int) throw 42;
  }
};
int main() {
  try {
    A a; 
    a.throw_an_int = true;
    std::vector<A> lots_of_As(42);
  } catch(int&) {}
}

标准在一般要求中这样说:

[C++11: §23.2.1/10]:

除非另有规定(见23.2.4.1、23.2.5.1、23.3.3.4和23.3.6.5)本条中定义的所有集装箱类型均满足以下附加要求:

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

clear函数为例(由于它不是一般要求的例外),它具有以下要求:

销毁a中的所有元素。使引用a元素的所有引用、指针和迭代器无效,并可能使过去的结束迭代器失效。帖子:a.empty()返回true

这意味着它本质上在所有元素上调用std::allocator_traits<Alloc>::destroy。如果a.destroy(t)不可用,则委托给t->~T()。然而,这隐含地保证了a.destroy(t)t->~T()都不应该抛出,因为这将违反clear的强noexcept规范:

// § 23.3.6.1
void clear() noexcept;

因此,通过推导,我们可以断言析构函数可以抛出,但必须通过某种机制来抑制它们,比如将它们封装在try-catch块中。

&匕首;:经过进一步检查,析构函数似乎可以抛出,但必须抑制异常,如下面的注释所述。