如何确定类型是否为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_copystd::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.

以上是如何确定类型是否为memcpy-save?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>