隐式生存期类型的`trivial eligible constructor`含义及memcpy构造合法性问询
平凡可拷贝类型memcpy隐式构造的合法性分析
问题与类型定义
先明确涉及的类型Foo及其属性:
struct Foo { Foo() = default; Foo(int i) : a(i) {}; int a = 5; };
- 非聚合类型:存在用户声明的构造函数
- 非平凡可构造类型:成员
a带有类内初始化器,导致默认构造函数虽被显式默认,但仍属于非平凡构造 - 平凡可拷贝/析构类型:默认生成的拷贝/移动构造、赋值操作均为平凡版本,析构函数也为平凡未删除状态
核心疑问:通过memcpy将已构造的Foo对象内存复制到未构造的内存区域,是否能合法隐式构造出Foo对象?示例代码如下:
Foo* makeFooCopy(const Foo& src) { // Assume alignment isn't a problem auto ptr = malloc(sizeof(Foo)); memcpy(ptr, &src, sizeof(Foo)); return reinterpret_cast<Foo*>(ptr); }
同时存在实际场景需求:自定义容器SpecialVectorType希望跳过resize中的placement new调用,直接用memcpy批量拷贝外部容器内容以提升性能,但不确定该操作的合法性。
标准规则解读
C++标准中,隐式生存期类型的判定是核心依据:
非聚合场景下,隐式生存期类型需满足:拥有至少一个平凡合格构造函数,且析构函数平凡、未删除
这里的「平凡合格构造函数」指平凡的构造函数(包括默认构造、拷贝构造、移动构造),且该构造函数未被删除、可用于对象初始化。
针对Foo类型:
- 它的拷贝构造函数是默认生成的平凡构造函数,且未被删除,属于合格构造函数
- 析构函数平凡且未删除
因此Foo属于隐式生存期类型。根据标准,对于隐式生存期类型,将已构造对象的内存表示复制到适当对齐的未构造内存区域后,该内存区域会被视为已构造出对应类型的对象——这意味着上述memcpy的实现是合法的。
自定义容器场景结论
对于你的SpecialVectorType场景:
- 如果元素类型是平凡可拷贝、平凡可析构的隐式生存期类型(如
Foo),直接memcpy整个缓冲区是合法的 - 可以安全省略
resize中的placement new调用,因为隐式生存期类型的对象可通过内存复制完成隐式构造 - 需确保内存区域满足适当对齐要求(
malloc或容器自身分配的对齐内存均符合要求),且元素类型确实符合隐式生存期的判定条件
编译器未优化掉resize中的placement new,是因为无法自动推断你后续会用memcpy完成对象构造,需显式移除placement new步骤,直接分配内存后执行memcpy才能实现性能优化。
内容的提问来源于stack exchange,提问作者James Picone
相关产品推荐
相关产品推荐

