You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

隐式生存期类型的`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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:08:18