memcpy 的类型安全 C++ 包装器?

Mar*_* Ba 5 c++ memcpy

鉴于std::copy(显然对于普通类型)只能作为memmove (*)的包装器实现,我想知道:

  • 是否有适合您需要的标准 C++ 类型安全包装器memcpy?(我数不清忘记乘以的次数sizeof。)
  • 如果标准中没有任何内容,是否有任何建议?如果没有,为什么不呢?
  • 提供自动memcpy进行sizeof乘法的包装器是否有任何特定障碍?

(*):C++ 标准库实现(从 MSVC 2005(!) 到现代 MSVC2015、libc++ 等)将 TriviallyCopyablestd::copy类型衰减为memmove. 博特memcpy。因为:

  • std::copy(src_first, src_last, destination_first) 定义:

    如果d_first在范围内,则行为未定义[first, last)

    • 只有目标范围的开头不能在源范围内。允许目标范围扩展到源范围。也就是说,d_first可以在源范围的“左边”,而目标范围可以扩展到源范围内。
  • 因为std::memcpy定义是

    如果对象重叠,则行为未定义。

    • 也就是说,整个范围不能重叠:这就是让 memcpy 成为最快变体的原因,因为它可以假设源和目标的内存完全不相交。
  • 对于std::memmove,定义为:

    对象可能会重叠:复制的发生就像将字符复制到临时字符数组,然后将字符从数组复制到 dest。

    • 即源和目的范围可以任意重叠,没有限制。

鉴于此,很明显您可以使用std::memove实现std::copyTrivialllyCopyable 类型,因为 memmove 没有强加任何限制,并且可以在编译时通过类型特征分派到正确的实现——

但是很难实现std::copymemcpy因为 (a) 检查指针范围是否重叠必须在运行时完成,以及 (b) 甚至对不相关的内存范围实施运行时检查可能会非常混乱

所以,这给我们留下了

void* memcpy( void* dest, const void* src, std::size_t count );
Run Code Online (Sandbox Code Playgroud)

一个接口不那么出色的函数,您经常需要将非字符对象的输入计数与其相乘,sizeof而这是完全无类型的。

但是 memcpy 是最快的(并且有很大的差距,请自行测量),并且当您需要 TriviallyCopyable 类型的快速副本时,您可以使用 memcpy。从表面上看,这应该很容易包装在类型安全的包装器中,例如:

template<typename T>
T* trivial_copy(T* dest, T* src, std::size_t n) {
    return static_cast<T*>(std::memcpy(dest, src, sizeof(T) * n));
}
Run Code Online (Sandbox Code Playgroud)

但是,不清楚您是否应该通过std::is_trival或其他方式进行编译时检查,当然可能会有一些讨论是否使用确切的memcpy签名顺序,yadda yadda。

那么我真的必须自己重新发明这个轮子吗?是否为标准进行了讨论?等等。


Rax*_*van 1

为了澄清 mencpy 和 memove 之间的区别,根据文档 memmove 可以将内存复制到与源内存重叠的位置,对于 memcpy 来说这是未定义的行为。

对象可能重叠:复制的发生就好像将字符复制到临时字符数组,然后将字符从数组复制到目标。

当您需要 memcpy 时,是否有标准 C++ 类型安全包装器?(我已经数不清有多少次忘记乘以 sizeof 了。)

是的,std::copy(也许,解释如下)

如果标准中没有任何内容,是否有任何建议?如果没有,为什么不呢?

据我所知,该标准并不强制对普通类型的 std::copy 使用 memmove/memcpy 。所以就看实施了。例如,在Visual Studio update 2015 update 2 中,他们确实使用 memmove 来加快速度

提高了 std::vector 重新分配和 std::copy() 的速度;当它们为普通可复制类型(包括用户定义类型)调用 memmove() 时,速度提高了 9 倍。

提供自动执行 sizeof 乘法的 memcpy 包装器是否有任何特定障碍?

不,事实上您可以使用std::is_trivial自己实现

编辑:

根据本文档第 25.3.1 节,对 std::copy 实现没有任何限制,只有复杂性:

复杂性:恰好是最后 - 第一个作业。

当您考虑到 memcpy 使用 cpu 特定指令(并非在所有 cpu 上都可用)来加速内存复制时,这是非常有意义的。