【问题标题】:Is using placement-new, copying the storage then accessing the value undefined behavior?是使用placement-new,复制存储然后访问值未定义的行为吗?
【发布时间】:2018-09-25 06:17:32
【问题描述】:

S 是一个结构类型,它包含一个字符数组data,它具有最大对齐和固定大小。这个想法是S 能够存储T 类型的任何对象,其大小不超过限制,并且可以简单地复制构造和简单地破坏。

static constexpr std::size_t MaxSize = 16;
struct S {
    alignas(alignof(std::max_align_t)) char data[MaxSize];
};

Placement-new 用于将T 类型的对象构造到新的S 对象的字符数组中。然后这个对象被复制任意次数,包括被返回和按值传递。

template <typename T>
S wrap(T t) {
    static_assert(sizeof(T) <= MaxSize, "");
    static_assert(std::is_trivially_copy_constructible_v<T>, "");
    static_assert(std::is_trivially_destructible_v<T>, "");

    S s;
    new(reinterpret_cast<T *>(s.data)) T(t);
    return s;
}

稍后给出这个S值的副本,reinterpret_cast用于从指向字符数组开头的指针获取T*,然后以某种方式访问​​T对象。 T 类型与创建值时的类型相同。

void access(S s) {
    T *t = reinterpret_cast<T *>(s.data);
    t->print();
}

我想知道此方案中是否涉及任何未定义的行为以及如何解决。例如,我担心:

  • “重用对象存储”是否存在问题,即std::launder旨在解决的问题?我不确定在那里构造T 的实例后以字符数组的形式访问data 是否有效。在访问值的地方我需要std::launder,为什么?
  • 在生成的S 的复制构造函数中是否存在问题,它复制了data 中的所有字节,因为某些字节可能尚未初始化?我担心sizeof(T) 之外的字节以及T 对象中可能未初始化的字节(例如填充)。

我的用例是实现一个非常轻量级的多态函数包装器,它能够与满足我为T 列出的那些要求的任何可调用对象一起使用。

【问题讨论】:

  • 我担心最大的问题是你没有提到的问题,我不知道如何解决:在重新存储之后,复制构造函数对底层字节的访问- 用于其他对象。而且你不能在那里插入std::launder
  • @hvd:存储如何被其他对象重用?我创建S 对象的唯一方法是通过create()。我最多可以将这些新的S 值分配给现有的S 值,但这只是复制字节。
  • 您正在重用new(reinterpret_cast&lt;T *&gt;(s.data)) T(t); 中的存储空间。之后,您可以在return s; 中直接、隐式地访问存储。我可能错了,但我认为允许编译器看到由placement-new创建的对象永远不会被访问并优化它。
  • @hvd:啊。我想构造一个本地 T 对象然后将 memcpy 构造成 S 会解决这个问题?
  • 好点,应该可以。

标签: c++ language-lawyer undefined-behavior placement-new


【解决方案1】:
template<class T>
T* laundry_pod(void* ptr){
  char buff[sizeof(T)];
  std::memcpy(buff, ptr, sizeof(T));
  auto* r=::new(ptr)T;
  std::memcpy(ptr, buff, sizeof(T));
  return r;
}

这可能有用。它将编译器对某个位置的数据的解释从一种 pod 类型转换为另一种,并在优化下编译为 noop。

因此,将您的缓冲区、洗衣舱传送到 T、写入它、洗衣舱返回 gdneric 存储。要读取,请洗衣 pod 到 T。请注意,所有通过 T 指针的写入都应随后将其洗回通用存储,以避免使用别名规则来优化看似丢弃的 ptr 上的写入。

【讨论】:

    猜你喜欢
    • 2023-03-22
    • 2022-07-19
    • 1970-01-01
    • 2014-08-12
    • 1970-01-01
    • 2017-07-15
    相关资源
    最近更新 更多