【问题标题】:Under what conditions is it safe to use std::memcpy to copy between objects?在什么条件下使用 std::memcpy 在对象之间复制是安全的?
【发布时间】:2020-05-08 03:51:23
【问题描述】:

在什么条件下使用std::memcpy从一个对象复制到另一个对象是安全的?

例如,Tsrcdest 必须满足哪些条件才能安全:

template <typename T>
void copy_bytewise(T& dest, const T& src) {
  std::memcpy(&dest, &src, sizeof(T));
}

关于srcdest,我们唯一可以假设的是它们不重叠1。特别是srcdest 中的任何一个都可能是对成员或基类的引用。

我对参考标准的答案很感兴趣,但如果这与常见做法不同(例如,来自 Itanium 的事实上的 C++ ABI),我也想知道。

请注意,满足TriviallyCopyable (TC) 概念的T 是不够的,正如this example 所示。 base 是 TC,但不是 memcpy 安全的(由于对派生类的成员重复使用填充)。

如果T 上的任何条件是足够的(不一定是必要的),我特别感兴趣,而不需要 srcdest 上的条件(不能,在一般情况下,静态确定)。


1 具体来说,我的假设是,如果它们确实重叠,那么在Tstd::memcpy 相同的条件下,它们仍然可以安全复制,但是改用std::memmove。如果假设不正确,它可能是答案的一部分。

【问题讨论】:

  • 这是discussion in comments 的后续。 godbolt.org/z/jBeRwD 示例表明成为聚合似乎很重要(公共与私人成员)。其他线索包括itanium-cxx-abi.github.io/cxx-abi/abi.html#POD 提到 ABI 何时决定将派生对象放在基类的填充中,或者不基于任何可能破坏所有版本的 ISO C++ 标准中的 UB。
  • memcpy的重点不是对类型没有要求吗?这是 C++ 中安全执行类型双关的唯一方法。
  • @HenriMenke,嗯,不。使用memcpy 对涉及的类型有相当严格的要求,否则会造成损害。将任何类型视为用户定义的复制构造函数:显然只是复制字节不太可能完成复制构造函数所做的事情。
  • @HenriMenke - 我认为这个“语言律师”问题不适合你,它需要大量关于 UB、memcpy、C++ 中的各种类型概念等方面的背景知识。甚至很难回答您的问题/cmets 无需假设很多现有背景。我建议阅读链接和相关问题的背景知识。
  • @NicolBolas - 我猜最明显,也可能是唯一的例子是它们是否是同一个对象(即“重叠”而不是“正确重叠”)。在这种情况下,我的理解是 std::memcpy 无论如何都是不允许的。

标签: c++ language-lawyer memcpy object-layout


【解决方案1】:

来自[basic.types]/3:

对于任何可简单复制的类型T,如果指向T 的两个指针指向不同的T 对象obj1obj2,其中obj1obj2 都不是基类子对象,如果构成obj1 的底层字节([intro.memory])被复制到obj2obj2 将随后保持与obj1 相同的值。

简而言之:

T 必须满足哪些条件才能使以下内容安全

T 必须是可简单复制的;这是T 必须满足的唯一条件。另一个要求不是对T 的限制,而是对可能被复制的对象的性质的限制。这意味着它不是您可以静态确定的。

【讨论】:

  • 谢谢。因此,在实践中,对于我编写的 copy_bytewise 函数,由于基类问题,存在或考虑到 no 特征对于任何 T 来说都是安全的?我应该清楚,我问的是对T 类型的(子)对象实例的限制,尽管我写得不好,而且它影响了你在回答中使用的语言。我正在做一个小的(字符)编辑来澄清,你可能想更新你的答案。
  • 我已将语言“T 的条件”更改为“Tsrcdest 的条件”,我认为这更清楚。 LMK 如果您认为变化太大。我认为您的回答仍然完全适用,但可以更新部分语言。
  • @BeeOnRope:不要忘记:“指向 distinct T 对象”
  • T 上是否有更强的条件来消除对srcdest 对象的附加(动态)条件的需要?在实践中,似乎有(例如,即使 dest 对象未知,gcc 有时也会覆盖填充),但我不知道标准是否涵盖了这一点,或者它是否有特征。
  • @BeeOnRope:嗯,你可以要求T 声明为final,因此不能是基类子对象。但仅此而已。
猜你喜欢
  • 2020-05-08
  • 2015-03-28
  • 1970-01-01
  • 1970-01-01
  • 2021-11-28
  • 1970-01-01
  • 2012-12-31
  • 1970-01-01
  • 2014-11-19
相关资源
最近更新 更多