使用placement-new,复制存储然后访问值undefined行为?

Amb*_*jak 7 c++ placement-new undefined-behavior language-lawyer

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

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

我想知道这个方案中是否有任何未定义的行为以及它将如何解决.例如,我担心:

  • "重用对象存储"是否存在问题,即std::launder旨在解决的问题?我不确定data在构造一个实例之后是否可以作为字符数组进行访问T.我需要std::launder在访问价值的地方,为什么?
  • 生成的复制构造函数中是否存在S复制所有字节的问题data,因为某些字节可能尚未初始化?我担心超出字节sizeof(T)以及T对象内可能未初始化的字节(例如填充).

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

Yak*_*ont 0

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;
}
Run Code Online (Sandbox Code Playgroud)

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

因此,将缓冲区、洗衣盒移至 T,写入数据,洗衣盒返回 gdneric 存储。要读取,请将 pod 清洗到 T。请注意,通过 T 指针进行的所有写入都应将其清洗回通用存储,以避免使用别名规则来优化看似已丢弃的 ptr 上的写入。