琐碎破坏的意义

Significance of trivial destruction

在 C++17 中,如果 T 在 [optional.object.dtor] 中是可平凡破坏的,则新的 std::optional 要求它是平凡可破坏的:

~optional();
1 Effects: If is_trivially_destructible_v<T> != true and *this contains a value, calls val->T::~T().
2 Remarks: If is_trivially_destructible_v<T> == true then this destructor shall be a trivial destructor.

所以这个潜在的实现片段将不符合标准:

template <class T>
struct wrong_optional {
    union { T value; };
    bool on;

    ~wrong_optional() { if (on) { value.~T(); } }
};

我的问题是:这个任务有什么好处?据推测,对于平凡可破坏的类型,编译器可以确定 value.~T() 是一个空操作,并且不会为 wrong_optional<T>::~wrong_optional() 发出任何代码。

std::optional 已经有 constexpr constructors. When its destructor is trivial, it is a literal type。在常量表达式中只能创建和操作文字类型的对象。

一种可以轻易破坏的类型是它自己的奖励。以下是拥有普通析构函数的一些优点:

  1. 类型可以简单地复制。这使得该类型有资格进行各种优化。 Visual Studio 的标准库实现有一个 number of optimizations 用于处理此类类型。

  2. 不费心调用普通可破坏类型的析构函数是合法的。您可以取消分配他们的存储空间。这是一件 low-level 要做的事情,但它也有好处。它是允许实现进行上述优化的一部分。

  3. 具有普通析构函数的类型可以是文字类型,因此是可以在编译时构造和操作的对象。

optional<T> 的界面试图尽可能不干扰 T 的行为。所以如果你可以用 T 做一些事情,那么你应该也可以用 optional<T> 做同样的事情。除非有充分的理由不这样做。

, one specific reason not yet mentioned and worth mentioning is: a type being trivially copyable and trivially destructible allows it to be passed in registers in the ABI (see Agner Fog's calling conventions)。这会对生成的代码产生重大影响。一个简单的函数,如:

std::optional<int> get() { return {42}; }

如果类型不简单,可能会发出以下代码 copyable/destructible:

    mov     rax, rdi
    mov     DWORD PTR [rdi], 42
    mov     BYTE PTR [rdi+4], 1
    ret

但如果它是:

    movabs  rax, 4294967338
    ret

那肯定更好。