4

我遇到了一些旨在在不重新分配内存的情况下就地替换对象的代码:

static void move(void* const* src, void** dest) {
   (*reinterpret_cast<T**>(dest))->~T();
   **reinterpret_cast<T**>(dest) = **reinterpret_cast<T* const*>(src);
}

这对我来说看起来像 UB,因为对象被销毁然后分配给没有被构造,即它需要只是复制分配(仅第二行)或显式破坏(第一行),然后是放置新复制构造而不是分配。

我之所以这么问,是因为尽管这对我来说似乎是一个明显的错误,但它在boost::spirit::hold_anycdiggins::any和它所基于的原始版本中已经存在了一段时间。(我已经在 Boost 开发者邮件列表中询问过它,但在等待回复时,如果它确实不正确,希望在本地解决这个问题。)

4

2 回答 2

3

Assuming the reinterpret_casts are well-defined (that is, dest really is a pointer to pointer to T), the standard defines the end of an object's lifetime as:

The lifetime of an object of type T ends when:

  • if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
  • the storage which the object occupies is reused or released.

It then gives some restrictions over what can be done with the glvalue **reinterpret_cast<T**>(dest):

Similarly, [...] after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. [...] The program has undefined behavior if:

  • an lvalue-to-rvalue conversion (4.1) is applied to such a glvalue,
  • the glvalue is used to access a non-static data member or call a non-static member function of the object, or
  • the glvalue is implicitly converted (4.10) to a reference to a base class type, or
  • the glvalue is used as the operand of a static_cast (5.2.9) except when the conversion is ultimately to cv char& or cv unsigned char&, or
  • the glvalue is used as the operand of a dynamic_cast (5.2.7) or as the operand of typeid.

Emphasis added.

If the object doesn't end up in this after-life state because it has a trivial destructor, there is no problem. However, for any T which is a class type with non-trivial destructor, we know that the assignment operator is considered a member function operator= of that class. Calling a non-static member function of the object through this glvalue results in undefined behaviour.

于 2013-03-12T11:24:01.867 回答
0

这对我来说看起来像 UB,因为对象被销毁然后分配给没有被构造,即它需要只是复制分配(仅第二行)或显式破坏(第一行),然后是放置新的复制构造而不是分配。

没有必要修复任何东西,尽管如果没有进一步的限定,这段代码肯定是不安全的(但在使用它的上下文中肯定是安全的)。

对象 atdest被销毁,然后支持对象 at 的内存src被复制到对象 atdest曾经存在的位置。最终结果:您已经销毁了一个对象,并在第一个对象曾经存在的地方放置了另一个对象的浅克隆。

如果您只进行复制分配,则第一个对象不会被破坏,从而导致资源泄漏。

使用放置new来填充内存dest是一种选择,但它的语义与现有代码有很大不同(创建一个全新的对象,而不是对现有对象进行浅层克隆)。放置 new 和使用复制构造函数也有不同的语义:对象需要有一个可访问的复制构造函数,并且您不再控制结果将是什么(复制构造函数做任何它想要的)。

于 2013-03-12T11:20:38.547 回答