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

C++11默认拷贝赋值运算符何时采用位拷贝而非成员拷贝?

在x86-64架构的GCC 13.1和Clang 16.0.0编译器中,copy<PrivateBase>函数采用成员拷贝,而copy<PublicBase>函数采用位拷贝。相关源码及汇编代码如下:

class PublicBase {
public:
    int num;
    char c1;
};

class PrivateBase {
private:
    int num;
    char c1;
};


template<typename T>
__attribute_noinline__ void copy(T *dst, T *src) {
    *dst = *src;
}

template void copy(PublicBase *dst, PublicBase *src);
template void copy(PrivateBase *dst, PrivateBase *src);

对应的汇编代码:

void copy<PublicBase>(PublicBase*, PublicBase*):
        mov     rax, QWORD PTR [rsi]
        mov     QWORD PTR [rdi], rax
        ret
void copy<PrivateBase>(PrivateBase*, PrivateBase*):
        mov     eax, DWORD PTR [rsi]
        mov     DWORD PTR [rdi], eax
        movzx   eax, BYTE PTR [rsi+4]
        mov     BYTE PTR [rdi+4], al
        ret
核心问题

C++11中的默认拷贝赋值运算符何时会使用位拷贝而非成员拷贝?is_trivially_copyable和is_pod似乎都无法直接给出答案。

is_trivially_copyable

根据cppreference中is_trivially_copyable的定义:

非潜在重叠子对象的可平凡拷贝类型对象,是唯一能通过std::memcpy安全拷贝的C++对象。

PublicBase和PrivateBase都属于可平凡拷贝类型,且都不是潜在重叠子对象,但PrivateBase却采用成员拷贝而非位拷贝。

is_pod

当PublicBase或PrivateBase存在派生类时,PrivateBase的派生类会复用基类的填充空间,而PublicBase的派生类不会。因此PrivateBase采用成员拷贝是合理的——否则调用copy<PrivateBase>(derived, base)时,基类的填充空间可能会覆盖PrivateDerived::c2。

相关验证代码及输出如下:

class PublicDerived : public PublicBase {
public:
    char c2;
};

class PrivateDerived : public PrivateBase {
private:
    char c2;
};


int main() {
    std::cout << "sizeof(PublicBase)=" << sizeof(PublicBase) << std::endl;
    std::cout << "sizeof(PublicDerived)=" << sizeof(PublicDerived) << std::endl;
    std::cout << "sizeof(PrivateBase)=" << sizeof(PrivateBase) << std::endl;
    std::cout << "sizeof(PrivateDerived)=" << sizeof(PrivateDerived) << std::endl;

    return 0;
}
// Output:
// sizeof(PublicBase)=8
// sizeof(PublicDerived)=12
// sizeof(PrivateBase)=8
// sizeof(PrivateDerived)=8

我对编译器如何决定是否复用基类填充空间存在疑惑。有相关问题提到,POD类型不会复用基类的填充空间。

根据cppreference中POD类的定义:

POD类是满足以下条件的类:

  • C++11之前:
    • 是聚合类(无私有或保护非静态数据成员)
    • 无用户声明的拷贝赋值运算符
    • 无用户声明的析构函数
    • 无非POD类(或此类数组)或引用类型的非静态数据成员
  • C++11及以后:
    • 是平凡类
    • 是标准布局类(所有非静态数据成员的访问控制权限一致)
    • 无非POD类(或此类数组)类型的非静态数据成员

在C11之前,PrivateBase不是POD类型(因为它有私有数据成员),但从C11开始,它成为了POD类型(所有非静态数据成员的访问控制权限一致)。

类型特性验证代码及输出:

#include <type_traits>
#include <iostream>

int main() {
    std::cout << "PublicBase: is_standard_layout=" << std::is_standard_layout<PublicBase>::value
              << ", is_trivial=" << std::is_trivial<PublicBase>::value
              << ", is_pod=" << std::is_pod<PublicBase>::value << std::endl;

    std::cout << "PrivateBase: is_standard_layout=" << std::is_standard_layout<PrivateBase>::value
              << ", is_trivial=" << std::is_trivial<PrivateBase>::value
              << ", is_pod=" << std::is_pod<PrivateBase>::value << std::endl;
}
// Output:
// PublicBase: is_standard_layout=1, is_trivial=1, is_pod=1
// PrivateBase: is_standard_layout=1, is_trivial=1, is_pod=1

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 18:07:03