【发布时间】:2017-07-31 21:56:48
【问题描述】:
鉴于std::copy(显然对于普通类型)只能作为memmove(*) 的包装器来实现,我想知道:
- 在您需要
memcpy的时候,是否有标准C++ 类型安全包装器? (我数不清有多少次我忘了乘以sizeof。) - 如果标准中没有任何内容,是否有任何建议?如果没有,为什么不呢?
- 在提供自动执行
sizeof乘法的memcpy包装器方面是否存在任何特定障碍?
(*):C++ 标准库实现(从 MSVC 2005(!) 到现代 MSVC2015、libc++ 等)衰减 std::copy TriviallyCopyable 类型到 memmove。机器人不到memcpy。因为:
-
std::copy(src_first, src_last, destination_first)定义为:如果
d_first在[first, last)范围内,则行为未定义。- 只有目标范围的开始不得在源范围内。目标范围允许扩展到源范围。也就是说,
d_first可以位于源范围的“左侧”,而目标范围可以延伸到源范围内。
- 只有目标范围的开始不得在源范围内。目标范围允许扩展到源范围。也就是说,
-
对于
std::memcpy,定义是这样的如果对象重叠,则行为未定义。
- 也就是说,整个范围不得重叠:这就是 memcpy 成为最快变体的原因,因为它可以假设源和目标的内存完全不相交。
-
对于
std::memmove,定义为:对象可能重叠:复制就像将字符复制到临时字符数组,然后将字符从数组复制到目标一样。
- 即源和目的范围可以任意重叠,没有限制。
鉴于此,很明显,您可以使用 std::memove 为 TrivialllyCopyable 类型实现 std::copy,因为 memmove 没有施加任何限制,并且可以在编译时通过类型特征完成对正确实现的调度 - -
但在 memcpy 方面很难实现 std::copy,因为 (a) 检查指针范围是否重叠必须在运行时完成,以及 (b) 甚至实现运行时检查不相关的内存范围could be quite a mess。
所以,这给我们留下了
void* memcpy( void* dest, const void* src, std::size_t count );
一个接口不太出色的函数,您需要不断地将非字符对象的输入计数乘以它们的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));
}
但是,不清楚您是否应该通过std::is_trival 或类似的方式进行编译时检查,当然可能会有一些讨论是否使用确切的memcpy 签名顺序,yadda yadda。
那么我真的必须自己重新发明这个轮子吗?是否针对标准进行了讨论?等等。
【问题讨论】:
-
是什么让你觉得
std::copy必须由memmove实现? -
这种包装器的用例是什么?为什么你对
std::copy本身不满意? -
你能举个例子,你需要手动memcpy(甚至根本复制)吗?在几乎所有情况下,所有这些都是在后台使用复制构造函数和复制分配完成的。
-
@nwp 不是“必须使用
memmove”,而是“不能使用memcpy,可以(只能)使用memmove”,这就是我认为OP的意思。 -
“鉴于 std::copy 只能实现为 memmove 的包装器” - 这是不正确的。
std::copy的文档说“如果源和目标范围重叠,则行为未定义。”,这使其衰减到memcpy。