琐碎破坏的意义
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。在常量表达式中只能创建和操作文字类型的对象。
一种可以轻易破坏的类型是它自己的奖励。以下是拥有普通析构函数的一些优点:
类型可以简单地复制。这使得该类型有资格进行各种优化。 Visual Studio 的标准库实现有一个 number of optimizations 用于处理此类类型。
不费心调用普通可破坏类型的析构函数是合法的。您可以取消分配他们的存储空间。这是一件 low-level 要做的事情,但它也有好处。它是允许实现进行上述优化的一部分。
具有普通析构函数的类型可以是文字类型,因此是可以在编译时构造和操作的对象。
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
那肯定更好。
在 C++17 中,如果 T
在 [optional.object.dtor] 中是可平凡破坏的,则新的 std::optional
要求它是平凡可破坏的:
~optional();
1 Effects: Ifis_trivially_destructible_v<T> != true
and*this
contains a value, callsval->T::~T()
.
2 Remarks: Ifis_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。在常量表达式中只能创建和操作文字类型的对象。
一种可以轻易破坏的类型是它自己的奖励。以下是拥有普通析构函数的一些优点:
类型可以简单地复制。这使得该类型有资格进行各种优化。 Visual Studio 的标准库实现有一个 number of optimizations 用于处理此类类型。
不费心调用普通可破坏类型的析构函数是合法的。您可以取消分配他们的存储空间。这是一件 low-level 要做的事情,但它也有好处。它是允许实现进行上述优化的一部分。
具有普通析构函数的类型可以是文字类型,因此是可以在编译时构造和操作的对象。
optional<T>
的界面试图尽可能不干扰 T
的行为。所以如果你可以用 T
做一些事情,那么你应该也可以用 optional<T>
做同样的事情。除非有充分的理由不这样做。
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
那肯定更好。