c++ - C++ 标准对于 move 事件对象存储有何规定?

标签 c++ language-lawyer move-semantics

我很好奇在当前的 C++ 标准下如何解释以下情况,特别是在生命周期等方面。这是未定义的行为吗?

首先,让我们从以下定义开始:可重定位对象是一个在其实际内存位置上不变的对象,也就是说,无论指针 this 的值如何,它的状态都保持不变。假设我们有一个可重定位类型 Relocatable (其定义与示例无关)。

然后我们有以下代码(C++17):

typedef std::aligned_storage_t<sizeof(Relocatable)> Storage;

// construct an instance of a relocatable within a storage
auto storage0 = new Storage();
new(storage0) Relocatable(...);

{ 
  // obj is a valid reference
  // should use std::launder() here, but clang doesn't have it yet
  Relocatable& obj = *reinterpret_cast<Relocatable*>(storage0);
}

// move the storage
auto storage1 = new Storage();
memcpy(storage1, storage0, sizeof(Storage));
delete storage0;

{ 
  // ?????? what does the standard say about this?
  Relocatable& obj = *reinterpret_cast<Relocatable*>(storage1);
}

这可以按预期与 GCC 和 Clang 一起使用(对象只是继续存在于新存储中)。但是,我不完全确定该标准是否可以接受。从技术上讲,对象的生命周期尚未结束(析构函数尚未被调用),并且在 memcpy() 调用之后没有对旧位置中的对象进行任何访问。此外,不存在对旧位置的引用/指针。尽管如此,鉴于 C++ 似乎在大多数情况下将对象标识和对象存储视为同一件事,因此禁止这样做可能是有原因的。预先感谢所有富有洞察力的评论。

编辑:有人建议 Why would the behavior of std::memcpy be undefined for objects that are not TriviallyCopyable?是这个问题的重复。我不确定是不是。首先,我正在复制存储,而不是对象实例。二、std::is_trivially_copyable<Relocatable>::value实际上评估为 true适用于所有实际相关的应用。

附注我问这个问题实际上有一个很好的实际原因。有时,拥有只能存在于其容器内的对象是有用的——它们不可复制且不可 move 。例如,我目前正在设计一个优化的树数据结构,具有这样的属性——树节点只能存在于树存储中,它们不能被移出或复制——对它们的所有操作都是通过短期引用来执行的。为了防止程序员错误(意外复制/move ),我删除了复制和 move 构造函数。这会带来相当不幸的后果,即节点无法存储在 std::vector 中。放置新的和显式管理的存储可以用来绕过这个限制——但我当然不想这样做 按照标准去做一些不合格的事情。

最佳答案

因此,与所有此类问题一样,对象仅在 four situations 中创建。 :

An object is created by a definition ([basic.def]), by a new-expression, when implicitly changing the active member of a union ([class.union]), or when a temporary object is created ([conv.rval], [class.temporary]).

这段代码:

auto storage1 = new Storage();
memcpy(storage1, storage0, sizeof(Storage));

storage1 处为您提供一个 Storage 类型的对象,但此时尚未创建任何 Relocatable 类型的对象。因此,这个:

Relocatable& obj = *reinterpret_cast<Relocatable*>(storage1);

是未定义的行为。期间。


为了定义其行为,我们需要第五种机制来创建对象,例如 P0593 中提出的机制。 :

We propose that at minimum the following operations be specified as implicitly creating objects: [...]

  • A call to memmove behaves as if it

    1. copies the source storage to a temporary area

    2. implicitly creates objects in the destination storage, and then

    3. copies the temporary storage to the destination storage.

    This permits memmove to preserve the types of trivially-copyable objects, or to be used to reinterpret a byte representation of one object as that of another object.

  • A call to memcpy behaves the same as a call to memmove except that it introduces an overlap restriction between the source and destination.

此提案(或类似的提案)对于您的代码格式良好是必要的。

关于c++ - C++ 标准对于 move 事件对象存储有何规定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49297869/

相关文章:

c++ - 编译器会为这个对象保留内存吗?

c++ - 带有条件的 Arrayfire 错误

C99 VLA 大小确定和 sizeof 运算符

c++ - 左值引用类型成员的用户定义 move 构造函数

c++ - 我可以列表初始化一个只 move 类型的 vector 吗?

C++类指针删除段错误

C++标准歧义

c++ - 用未初始化的变量初始化变量是否合法?

c++ - 为什么具有显式声明的 move 构造函数的类会失败 `std::is_move_constructible_v` ?

c++17 polyfills 和 boost