使用模板输入双关语

type punning with template

我不得不检查某人使用 20 多个联合的笨拙代码,并根据从字节数组复制数据和向字节数组复制数据的方法数量,以便通过模板实现这些方法(第二个用于 char)

template <class T> class type_punner
{
    T& p;
    unsigned char* pun;

public:
    type_punner(T&  ref): p (ref), pun(reinterpret_cast<unsigned char*>(&p))
    {
        static_assert(std::is_pod<T>::value, "type_punner can be used only for POD");
    }


    inline unsigned char& at(std::size_t  i)
    {
#ifdef QT_DEBUG
        if(!(i < size())) throw std::out_of_range( __FUNCTION__ );
#endif
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
        return pun[i];
#else
        return pun[size() - i - 1];
#endif
    }

    inline unsigned char& reverse_at(std::size_t  i)
    {
#ifdef QT_DEBUG
        if(!(i < size())) throw std::out_of_range(__FUNCTION__);
#endif
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
        return pun[size() - i - 1];
#else
        return pun[i];
#endif
    }

    // = 0 is  LSB
    inline unsigned char& operator[](std::size_t  i)
    {
        return at(i);
    }

    inline std::size_t size()
    {
        return sizeof(T);
    }
};

只有我担心如果我保持标准兼容并通过返回的引用分配新值才有效。

Only what I worry about that if I'm staying standard compliant and assigning new values via returned references is valid.

有效,因为那些引用都是窄字符类型,比较特殊

您的代码似乎是合法的。有一些改进:

constexpr bool k_little_endian
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
  = true;
#else
  = false;
#endif
constexpr bool k_debug
#ifdef QT_DEBUG
  = true;
#else
  = false;
#endif

template <class T,
  bool little_endian = k_little_endian,
  bool debug = k_debug
>
class type_punner {
  T* p;
  unsigned char* pun() const { return reintepret_cast<unsigned char*>(p); }
public:
  static_assert(std::is_pod<T>::value, "type_punner can be used only for POD");
  type_punner(T& ref):
    p (std::addressof(ref))
  {}
  type_punner(type_punner const&)=default;
  type_punner()=delete;

  unsigned char& at(std::size_t i) const noexcept(!debug) {
    if (debug && !(i<size())) throw std::out_of_range( __FUNCTION__ );
    if (little_endian)
      return pun()[i];
    else
      return pun()[size() - i - 1];
  }

  unsigned char& reverse_at(std::size_t i) const noexcept(!debug) {
    if(debug && !(i < size())) throw std::out_of_range(__FUNCTION__);
    if (little_endian)
      return pun()[size() - i - 1];
    else
      return pun()[i];
  }
  // = 0 is  LSB
  unsigned char& operator[](std::size_t i) const noexcept(!debug) {
    return at(i);
  }
  static constexpr std::size_t size() noexcept(true) { return sizeof(T); }
};

首先,这会将宏移开。您正在查看的代码往往更容易推理,并且编译器完全能够消除死分支。除非你想在你的类型上使用引用语义,否则存储引用很少是个好主意,而且在同一个 class/struct.

其次,class 正文中的内联是多余的。

第三,size既是constexpr又是static。

第四,默认copy/assign 的T& 没有做任何合理的事情。 T* 确实如此。所以我用了T*.

第五,不用存储pun。每次都以零成本生成它。

请注意,上面的 type_punner 可以在 big endian 环境中用 little_endian 进行实验,或者只对有问题的部分打开调试。这样做的代价是很小的编译时代价。