c++ - 为什么 std::memcpy 的行为对于不是 TriviallyCopyable 的对象是未定义的?

标签 c++ c++11 language-lawyer memcpy object-lifetime

来自 http://en.cppreference.com/w/cpp/string/byte/memcpy :

If the objects are not TriviallyCopyable (e.g. scalars, arrays, C-compatible structs), the behavior is undefined.

在我的工作中,我们长期使用 std::memcpy 来按位交换不是 TriviallyCopyable 的对象:

void swapMemory(Entity* ePtr1, Entity* ePtr2)
{
   static const int size = sizeof(Entity); 
   char swapBuffer[size];

   memcpy(swapBuffer, ePtr1, size);
   memcpy(ePtr1, ePtr2, size);
   memcpy(ePtr2, swapBuffer, size);
}

从来没有任何问题。

我了解使用非 TriviallyCopyable 对象滥用 std::memcpy 并导致下游未定义行为是微不足道的。但是,我的问题:

为什么 std::memcpy 本身的行为在与非 TriviallyCopyable 对象一起使用时会未定义?为什么标准认为有必要指定?

更新

http://en.cppreference.com/w/cpp/string/byte/memcpy 的内容已根据此帖子和帖子的答案进行了修改。目前的描述是:

If the objects are not TriviallyCopyable (e.g. scalars, arrays, C-compatible structs), the behavior is undefined unless the program does not depend on the effects of the destructor of the target object (which is not run by memcpy) and the lifetime of the target object (which is ended, but not started by memcpy) is started by some other means, such as placement-new.

附言

@Cubbi 的评论:

@RSahu if something guarantees UB downstream, it renders the entire program undefined. But I agree that it appears to be possible to skirt around UB in this case and modified cppreference accordingly.

最佳答案

Why would the behavior of std::memcpy itself be undefined when used with non-TriviallyCopyable objects?

不是!但是,一旦您将一个不可复制类型的对象的底层字节复制到该类型的另一个对象中,目标对象就不再存在。我们通过重用它的存储来销毁它,并没有通过构造函数调用来恢复它。

使用目标对象——调用它的成员函数,访问它的数据成员——显然是未定义的[basic.life]/6,随后的隐式析构函数调用也是如此[basic .life]/4 用于具有自动存储持续时间的目标对象。请注意未定义的行为是如何追溯的。 [intro.execution]/5:

However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).

如果一个实现发现一个对象是如何死亡的并且必然受到未定义的进一步操作的影响,......它可能会通过改变你的程序语义来使用react。从 memcpy 调用开始。一旦我们想到优化器和他们做出的某些假设,这种考虑就会变得非常实用。

应该注意的是,标准库能够并且允许优化某些标准库算法以用于一般可复制的类型。 std::copy 在指向普通可复制类型的指针上通常会在底层字节上调用 memcpyswap也是如此。
因此,只需坚持使用普通的泛型算法并让编译器进行任何适当的低级优化 - 这就是最初发明可复制类型的想法的部分原因:确定某些优化的合法性。此外,这避免了担心语言中矛盾和未指定的部分而伤害您的大脑。

关于c++ - 为什么 std::memcpy 的行为对于不是 TriviallyCopyable 的对象是未定义的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29777492/

相关文章:

c++ - 当程序删除时崩溃时如何调试?

c++ - 使用 if goto 循环的正确方法

自更新到 R 3.4.0 以来,C++11 的 Rcpp 插件不起作用

c++ - 是UB通过将对象指针转换为 `char *`然后执行 `*(member_type*)(pointer + offset)`来访问成员的吗?

c++ - 哪些类型是可简单构造的?

c++ - 为什么 std::exchange 不是 noexcept?

c++ - 如何使用 RcppZiggurat 采样器为 Rcpp 函数设置 setseed?

c++ - OpenCV 中的 Mat 类 (c++)

c++ - 如何在 C++ 中创建条件类型定义

c++ - 命名空间 -`static` 在 C++11 中是否仍被弃用?