【发布时间】:2020-05-08 03:51:23
【问题描述】:
在什么条件下使用std::memcpy从一个对象复制到另一个对象是安全的?
例如,T、src 和 dest 必须满足哪些条件才能安全:
template <typename T>
void copy_bytewise(T& dest, const T& src) {
std::memcpy(&dest, &src, sizeof(T));
}
关于src 和dest,我们唯一可以假设的是它们不重叠1。特别是src 或dest 中的任何一个都可能是对成员或基类的引用。
我对参考标准的答案很感兴趣,但如果这与常见做法不同(例如,来自 Itanium 的事实上的 C++ ABI),我也想知道。
请注意,满足TriviallyCopyable (TC) 概念的T 是不够的,正如this example 所示。 base 是 TC,但不是 memcpy 安全的(由于对派生类的成员重复使用填充)。
如果T 上的任何条件是足够的(不一定是必要的),我特别感兴趣,而不需要 src 和 dest 上的条件(不能,在一般情况下,静态确定)。
1 具体来说,我的假设是,如果它们确实重叠,那么在T 和std::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