关于C++中placement new使用char数组或void*缓冲区是否属于未定义行为的技术问询
关于C++中placement new使用char数组或void*缓冲区是否属于未定义行为的技术问询
咱们结合C++标准的规定,一步步拆解你的两个问题:
问题1:使用alignas(A) char buf[sizeof(A)]作为placement new的缓冲区是否属于UB?
首先明确标准里的核心规则:
标准[intro.object#3]规定:如果在与类型为“N个unsigned char的数组”或“N个std::byte的数组”的对象e关联的存储中创建完整对象(通过new表达式),则该数组为创建的对象提供合法存储。
另外[basic.types.general#4]也补充:只有unsigned char对象的序列,才能作为任意完整对象T的潜在有效对象表示。
你这段代码里,哪怕加了alignas(A)解决了对齐问题,但缓冲区是带符号的char数组,并不在标准明确允许的类型列表里。C++标准的逻辑是:凡是标准没有明确定义为合法的行为,都属于未定义行为。所以哪怕对齐没问题,这段代码依然是未定义行为——这和原问题的差异只是你解决了对齐,但核心的缓冲区类型不符合标准要求的问题还在。
问题2:如果上述情况是UB,是仅针对自动存储,还是也包括堆分配的存储?
先看你给出的堆分配代码:
auto alignment = std::align_val_t(alignof(A)); auto buf = ::operator new(sizeof(A), alignment); auto p = new (buf) A(...); // buf是void*,不是unsigned char*或std::byte* // ... p->~A(); ::operator delete(buf, alignment);
这段代码不是未定义行为,是完全合法的。而且你提到的标准里的示例代码:
void* place = operator new(sizeof(Something)); Something* p = new (place) Something();
也是标准明确给出的合法用法,肯定不会是UB。
那为什么和自动存储的char数组情况不一样?核心差异在这儿:
- 自动存储的
char buf[]是一个已经存在的char类型数组对象,它的存储是和这个char数组绑定的,属于“与某个对象关联的存储”,所以必须遵守[intro.object#3]里的数组类型限制——只有unsigned char/std::byte数组的关联存储才能用来创建其他对象。 - 而
::operator new分配的存储是原始的、未与任何具体类型对象关联的裸存储,它不属于任何对象的关联存储,所以不受[intro.object#3]里的数组类型限制。只要存储的大小和对齐满足要创建的对象的要求,placement new就可以合法地在上面创建对象,和指针的类型(比如这里的void*)无关。
总结
- 加了
alignas(A)的char数组作为placement new缓冲区,依然是未定义行为,因为标准只允许unsigned char或std::byte数组。 - 堆分配的
void*缓冲区作为placement new参数是合法的,不是UB;和自动存储的差异在于,堆分配的是原始裸存储,而自动存储的char数组是已关联到char数组对象的存储,受标准的数组类型限制。
内容来源于stack exchange
相关产品推荐
相关产品推荐

