c++11 中的类型何时允许内存

When is a type in c++11 allowed to be memcpyed?

本文关键字:内存 许内存 何时允 类型 c++11      更新时间:2023-10-16

我的问题如下:

如果我想复制一个类类型,memcpy可以非常快地完成。在某些情况下,这是允许的。

我们有一些类型特征:

  • is_standard_layout。
  • is_trivially_copyable。

我想知道的是类型何时"按位可复制"的确切要求。

我的结论是,如果is_trivally_copyableis_standard_layout特征都为真,则类型是按位可复制的:

  1. 这正是我需要按位复制的吗?
  2. 是不是过分约束了?
  3. 是否约束不足?

PS:当然,memcpy的结果一定是正确的。我知道我在任何情况下都可以做模思妙想,但不正确。

当 T

为 true 时,可以使用 memcpy 复制 T 类型的对象is_trivially_copyable<T>::value。类型不需要是标准布局类型。"微不足道的可复制"的定义本质上是这样做是安全的。

可以使用memcpy安全复制但不是标准布局的类示例:

struct T {
  int i;
private:
  int j;
};

由于此类对不同的非静态数据成员使用不同的访问控制,因此它不是标准布局,但它仍然是可复制的。

如果is_trivally_copyable<T>::value(或在 C++14 is_trivially_copyable<T>() 中,或在 C++17 is_trivially_copyable_v<T> 中)不为零,则该类型可使用 memcpy 进行复制。

根据C++标准,可复制的类型意味着:

构成对象的基础字节可以复制到数组中字符或无符号字符。如果将字符数组或无符号字符的内容复制回对象,则对象随后应保持其原始值。

但是,重要的是要意识到指针也是微不足道的可复制类型。每当要复制的数据结构中有指针时,您都必须在大脑上确保复制它们是正确的。

仅依靠可复制的对象可能造成危害的示例:

  • 一种树结构实现,其中数据放置在连续的内存区域中,但节点存储子节点的绝对地址
  • 为了多线程性能(以减少缓存崩溃)创建某些数据的多个实例,内部拥有指针,指向任何地方
  • 你有一个没有指针的平面对象,但内部有一个嵌入的第三方结构。将来的某个时候,第三方结构包括一个不应存在两次或更多次的指针。

因此,每当进行忆术复制时,请记住检查在特定情况下是否可以复制指针,以及是否可以。

意识到is_trivially_copyable只是编译器术语中的"语法检查",而不是"语义测试"。

从 http://en.cppreference.com/w/cpp/types/is_trivially_copyable:

简单可复制类型的对象是唯一可以使用 std::memcpy 安全地复制或使用 std::ofstream::write()/std::ifstream::read() 序列化到二进制文件/从二进制文件序列化到/从二进制文件序列化C++对象。通常,简单可复制类型是可以将基础字节复制到 char 或无符号 char 数组并复制到相同类型的新对象中的任何类型,并且生成的对象将具有与原始对象相同的值。

具有普通复制构造函数、简单复制赋值运算符和可以使用 memcpymemmove 复制简单的析构函数

T的特殊成员函数的要求是微不足道

复制构造函数 (cc) 和复制赋值运算符 (ca)

  • 不是用户提供的(意味着,它是隐式定义的或默认的),如果它是默认的,它的签名与隐式定义的签名相同
  • T没有虚拟成员函数
  • T没有虚拟基类
  • 为每个T的直接碱基选择的cc/ca是微不足道的
  • T的每个非静态类类型(或类类型数组)成员选择的 cc/ca 是微不足道的
  • T 没有可变限定类型的非静态数据成员(自 C++14 起)

析 构 函数

  • 不是用户提供的(意味着,它是隐式定义的或默认的)
  • 不是虚拟的(即基类析构函数不是虚拟的)
  • 所有直接基类都有简单的析构函数
  • 类类型(或类类型的数组)的所有非静态数据成员都有简单的析构函数

仅将函数声明为 = default 并不能使其变得微不足道(只有在以下情况下才会变得微不足道该类还支持相应函数的所有其他条件是微不足道的)但是在用户代码中显式编写函数确实可以防止它变得微不足道。此外,所有与 C 语言兼容的数据类型(POD 类型)都是可复制的。

来源 : C++ 操作和 cppreference.com 中的并发

我的理解是

  • 对象应具有默认构造函数/析构函数。
  • 默认复制和移动操作。
  • 没有静态和虚拟函数有多个
  • 非静态数据成员的访问说明符可防止重要的
  • 布局优化具有非静态成员或不是标准布局。

您可以使用标准函数 is_pod::value 来测试给定的类型是否为 pod(纯旧数据)

参考:C++编程语言第4版