如何确定类型是否为memcpy-save?
我和那些memcpy(太)喜欢的同事一起工作。我正在移植通常处理简单类型(double主要是 s)的代码,可以使用 安全地复制这些代码memcpy,并将其扩展到非平凡类型。我想编写一个简单的复制函数,它根据所讨论的类型做正确的事情:
#include <string.h>
#include <vector>
#include <type_traits>
template<class T>
void fancy_copy(const T* src,
T* dest,
unsigned int size)
{
// here:
if(std::is_trivially_copy_assignable<T>::value)
{
memcpy(dest, src, sizeof(T) * size);
}
else
{
for(unsigned int i = 0; i < size; ++i)
{
dest[i] = src[i];
}
}
}
class Custom
{
private:
int value;
public:
Custom& operator=(const Custom& other)
{
value = other.value + 1;
return *this;
}
};
int main()
{
const unsigned int size = 10;
{
std::vector<int> source(size, 0);
std::vector<int> target(size, 0);
fancy_copy<int>(source.data(), target.data(), size);
}
{
std::vector<Custom> source(size);
std::vector<Custom> target(size);
fancy_copy<Custom>(source.data(), target.data(), size);
}
return 0;
}
我使用type_traitsC++ 中的内置来确定要使用的实现。不幸的是,当我-Wall使用g++(10.2)编译代码时,我收到警告
warning: ‘void* memcpy(void*, const void*, size_t)’ writing to an object of type ‘class Custom’ with no trivial copy-assignment; use copy-assignment or copy-initialization instead [-Wclass-memaccess]
所以,对于我的Custom班级来说,memcpy是被错误地使用了。我需要使用哪种类型的特征来选择正确的操作并使警告静音?
回答
用于此的正确类型特征是std::is_trivially_copyable, 不是std::is_trivially_copy_assignable。
要修复警告,请使用if constexpr而不是if为了在编译时执行检查,并且仅生成两个分支之一作为给定类型的无条件逻辑T。即使由于运行时条件逻辑无法访问格式错误的调用,编译器也会发出警告,因为该调用仍然存在于生成的代码中。
还可以考虑使用std::copy_nfrom<algorithm>来简化回退的逻辑。
在 Godbolt.org 上试用:演示。
对于 C++11,您可以使用std::enable_if类似于 C++17 的方式选择在编译时使用的实现if constexpr:
template<class T>
typename std::enable_if<std::is_trivially_copyable<T>::value>::type
fancy_copy(const T* src, T* dest, unsigned int size)
{
memcpy(dest, src, sizeof(T) * size);
}
template<class T>
typename std::enable_if<!std::is_trivially_copyable<T>::value>::type
fancy_copy(const T* src, T* dest, unsigned int size)
{
std::copy_n(src, size, dest);
}
在 Godbolt.org 上试用:演示。
最终,正如其他人指出的那样,这fancy_copy可能是一个过早的优化,你最好只使用std::copy_n语义正确的 where,允许编译器执行自己的优化。比较使用fancy_copy和std::copy_n使用时的二进制文件-O3,自己看看。它们完全相同。
- Yes. `std::copy_n` is extremely fast, and sometimes faster than `memcpy` when the compiler can compute the alignment
- This should maybe be the take away. Still using memcpy in C++ code is just cargo cult programming.