用模板打字

时间:2017-04-20 07:07:56

标签: c++ c++11 templates type-punning

我必须检查某人使用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);
    }
};

如果我保持标准兼容并通过返回的引用分配新值,那么我只担心这一点。

2 个答案:

答案 0 :(得分:1)

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

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); }
};

首先,这会将宏移开。您正在查看的代码往往更易于推理,编译器完全能够消除死分支。除非您希望在类型上使用引用语义,否则很少存储引用,并且在同一个类/结构中将引用存储在非引用旁边几乎不是一个好主意。< / p>

其次,类体中的内联是多余的。

第三,size既是constexpr又是静态的。

第四,使用默认副本/分配的T&并不做任何合理的事情。 T*。所以我使用了T*

第五,无需存储pun。每次生成它都是零成本。

请注意,上述type_punner可以在大端环境中试用little_endian,或者只针对遇到问题的部分进行调试。这是一个很小的编译时间。

答案 1 :(得分:0)

  

如果我保持标准兼容并通过返回的引用分配新值,那么我只担心这一点。

它是有效的,因为那些引用是窄字符类型,这是特殊的。