2017-04-20 13 views
0

私は、20以上の共用体を使用していた不器用なコードをチェックしなければならなかったし、テンプレートを使ってそれらのメソッドを実装するために、 )テンプレートでの型打ち

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

標準に準拠し、返された参照を使用して新しい値を割り当てると、私が心配していることのみが有効です。

+0

さて、標準の通り、未定義の動作です。あなたがハイドラを産まないようにすることはあなたの仕事です。 – DeiDei

+0

@DeiDei私は実際にハイドラを育てません。それは同僚によるハイドラを作成する最悪の例ではありません。キャラクタ型ポインタへのキャストに関するルールなどはどうでしたか?実際には、そのような非地面のコメントは役に立ちません、btw、A)目的はデータを表示し、それを逆順または真っ直ぐな順序でパックすることです。B)C++は利用できません – Swift

答えて

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

まず、マクロを移動します。あなたが目にしているコードは、推論するのが簡単な傾向があり、コンパイラは死んだブランチを完全に排除することができます。あなたの型に参照セマンティクスを必要としない限り、参照を格納することはめったにありません。同じクラス/構造体内のの非参照の隣に参照を格納することは、決してお勧めできません。

第2に、クラス本体のインラインは冗長です。

第3に、sizeはconstexprとstaticの両方です。

デフォルトでは、T&のcopy/assignは妥当な何も行いません。 T*です。だから私はT*を使った。

第5位、保存不要punです。ゼロコストで毎回生成する。

上記のtype_punnerは、ビッグエンディアン環境で1つを試しても問題が発生している部分でのみデバッグを有効にすることができます。これのためのコストはコンパイル時の小さなものです。

+0

ああ、面白いです参照フィールド! 'constexpr'はサポートされていませんが、' const'は十分でなければなりません。アップグレードした場合、およびインラインで使用される2つのコンパイラのうちの1つに対して、冗長ではありませんインラインではない、またはすべての関数を展開します)、クラスには参照を格納しません。static_assert()は関数本体のみをサポートし、デフォルトまたは削除されたメソッドはサポートしていません。私は確かに生成されたpun()で実験するだろう – Swift

+0

何がnoexcept(!デバッグ)ですか? – Swift

+1

の前に見たことはありません@Swift It stats "これは' debug'がfalseの場合には投げないことが保証されていますが、 'debug'が真であれば投げるかもしれません。 – Yakk

0

標準に準拠し、返された参照によって新しい値を割り当てる場合、私が心配することのみが有効です。

これらの参照は特殊文字であるため、有効です。

関連する問題