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

使用memcpy构造可平凡复制union是否属于UB?其活跃成员如何判定?

问题解答

结论先行

你给出的示例在C17及更高版本标准下完全合法,不会触发未定义行为;如果需要兼容C14及更早标准,补充一行显式激活union成员的代码即可完全规避风险。

具体规则说明

  • 首先Bar属于平凡可复制类型,这是memcpy操作合法的核心前提:对于所有平凡可复制类型,只要源对象的对象表示有效,用memcpy将其对象表示复制到目标类型的存储中后,目标对象就会拥有和源对象一致的值表示,这个操作本身是标准明确支持的。你示例中src是有效int对象,大小和Bar的大小完全一致,因此memcpy操作本身没有任何问题。
  • 关于union活跃成员的常见误解:并不是“最先读取的成员自动成为活跃成员”,C++17新增了明确规则:如果union当前存储的值表示和某个成员的类型匹配,那么读取该成员的操作是合法的,会隐式创建该成员作为union的活跃成员。你示例中的Bar两个成员,num是int类型,foo是底层类型为int的枚举类型,两者的对象表示完全一致,你memcpy写入的就是合法int的对象表示,因此后续无论读取num还是foo,都是符合标准的合法操作。
  • 关于BOOST_IS_BITWISE_SERIALIZABLE特性的使用:你的推测是正确的,这个宏的作用就是标记类型允许直接通过二进制复制完成序列化/反序列化,底层确实依赖memcpy类操作。对于你用到的这类平凡可复制union,只要反序列化后读取的成员类型和序列化时的活跃成员类型兼容,就完全符合该宏的使用要求,不会有问题。

旧标准兼容方案

如果需要兼容C++17之前的标准,只需要在memcpy操作之后,显式激活你要读取的成员即可,示例修改如下:

int baz(int src) {
    Bar dst;
    std::memcpy(&dst, &src, sizeof(Bar));
    // 显式激活对应成员,兼容C++14及更早标准
    if (src > 42) {
        new (&dst.num) int; // 定位new,无额外开销,仅标记活跃成员
        return dst.num;
    } else {
        new (&dst.foo) Foo;
        return (int)dst.foo;
    }
}

内容的提问来源于stack exchange,提问作者StableGeneous

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 14:48:05