2013-01-17 5 views
5

私はC++で共用体を扱っていますが、テンプレートパラメータに応じてアクティブな共用体メンバにアクセスする関数テンプレートが必要です。私は専門、またはアクセスして割り当てることが均質でない方法のような悪いハックを発見し、これを達成するためにテンプレートパラメータに応じて共用体メンバを選択してください

union Union { 
    int16_t i16; 
    int32_t i32; 
}; 

enum class ActiveMember { 
    I16 
    , I32 
} 

template <ActiveMember M> 
void doSomething(Union a, const Union b) { 
    selectMemeber(a, M) = selectMember(b, M); 
    // this would be exactly (not equivalent) the same 
    // that a.X = b.X depending on T. 
} 

コード(doSomethingのは一例です)のようなものです。

私は何かが不足しています、そしてそのようなものは他のアプローチでそれをするべきですか?

+3

なぜ専門性は「悪いハック」だと思いますか? C++では "静的if"というものはありませんので、別のテンプレートを定義する以外の方法はありません。私は間違っているかもしれません。 –

+0

あなたは 'selectMemeber(a、M)= selectMember(b、M);'を意味すると思います。 – Philipp

+0

@Philipp、はい、ありがとうございます。編集されました。 – user1476999

答えて

6

可能性1

の代わりに、列挙型を使用して、あなたはメンバーを選ぶために、単純な構造体を使用することができます。

typedef short int16_t; 
typedef long int32_t; 

union Union { 
    int16_t i16; 
    int32_t i32; 
}; 

struct ActiveMemberI16 {}; 
struct ActiveMemberI32 {}; 

template <typename M> 
void doSomething(Union& a, Union b) { 
    selectMember(a, M()) = selectMember(b, M()); 

    // this would be exactly (not equivalent) the same 
    // that a.X = b.X depending on T. 
} 

int16_t& selectMember(Union& u, ActiveMemberI16) 
{ 
    return u.i16; 
} 

int32_t& selectMember(Union& u, ActiveMemberI32) 
{ 
    return u.i32; 
} 

int main(int argc, char* argv[]) 
{ 
    Union a,b; 
    a.i16 = 0; 
    b.i16 = 1; 
    doSomething<ActiveMemberI16>(a,b); 
    std::cout << a.i16 << std::endl; 

    b.i32 = 3; 
    doSomething<ActiveMemberI32>(a,b); 
    std::cout << a.i32 << std::endl; 
    return 0; 
} 

これはすべてのための構造体とselectMemberメソッドを定義する必要がありしかし、少なくともselectMemberは他の多くの関数で使用できます。

私は引数を参照に変えましたが、適切でない場合はこれを調整することができます。

希望型のポインタへの組合のポインタをキャストすることによって可能性2

、あなたは、単一のselectMember機能に行くことができます。

typedef short int16_t; 
typedef long int32_t; 

union Union { 
    int16_t i16; 
    int32_t i32; 
}; 
template <typename T> 
T& selectMember(Union& u) 
{ 
    return *((T*)&u); 
} 

template <typename M> 
void doSomething(Union& a, Union b) { 
    selectMember<M>(a) = selectMember<M>(b); 

    // this would be exactly (not equivalent) the same 
    // that a.X = b.X depending on T. 
} 



int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Union a,b; 
    a.i16 = 0; 
    b.i16 = 1; 

    doSomething<int16_t>(a,b); 
    std::cout << a.i16 << std::endl; 

    b.i32 = 100000; 
    doSomething<int32_t>(a,b); 
    std::cout << a.i32 << std::endl; 
    return 0; 
} 
+1

+1の可能性2.私の心に来ていない、よくやった –

+0

私は可能性2について考えましたが、それは違法だったようでした。私は読んでいると私は間違っていたように見えます。ありがとう – user1476999

+0

Potatoswatterは私の質問で同じアイデアを思いついた。それは確かに最も簡単な解決策です。 – MSalters

3

テンプレートの特殊化を「悪いハック」と考える理由はわかりませんが、C++では「静的if」というようなことはありません。そのため、結果に基づいて生成されたコードをコンパイラで区別したい場合コンパイル時にと評価された式の場合、異なる特殊バージョンのテンプレートを定義する必要があります。

#include <iostream> 

using namespace std; 

union Union { 
    int16_t int16; 
    int32_t int32; 
}; 

enum class ActiveMember { 
    INT16 
    , INT32 
}; 

// Declare primary template 
template <ActiveMember M> 
void doSomething(Union a, const Union b); 

// First specialization 
template <> 
void doSomething<ActiveMember::INT16>(Union a, const Union b) 
{ 
    a.int16 = b.int16; 

    // Do what you want here... 
    cout << "int16" << endl; 
} 

// Second specialization 
template <> 
void doSomething<ActiveMember::INT32>(Union a, const Union b) 
{ 
    a.int32 = b.int32; 

    // Do what you want here... 
    cout << "int32" << endl; 
} 

そして、これはあなたがそれを使用する方法である:ここでは

はあなたがそれをどのように定義するかです。

int main() 
{ 
    Union u1, u2; 
    u1.int32 = 0; 
    u2.int32 = 0; 

    doSomething<ActiveMember::INT16>(u1, u2); 
    doSomething<ActiveMember::INT32>(u1, u2); 

    return 0; 
} 
+1

特殊化は拡張可能なソリューションではないため、コードを書き直す必要があります。もちろん、この例ではおそらく最良の選択肢(よりシンプルなもの)です。しかし、1つの関数と2つのメンバーしかないので、いくつかの関数を書く必要があり、10人のメンバーがいる場合はどうなりますか? – user1476999

+0

@ user1476999: 'get (u)'のように、特殊化する必要のあるテンプレートが1つだけ必要なように、コードをいつでも除外することができます。そして、あなたはそれをあなたの他のすべての機能で一般的に使うことができます。 –

+0

@ user1476999:2つの考慮事項。 first: 'selectMember'関数の戻り値の型は何でしょうか?あなたが組合で作業しているので、戻り値の型は 'M'の値に依存します。 second:C++が "static_if"のようなものをサポートしていれば、コンパイル時に評価される条件文が(コンパイルされないように)評価されます。残念ながら、現時点ではこの機能はありません。 –

0

私は考えることができる唯一の解決策は、労働組合に演算子を追加することです:

union Union 
{ 
    char c; 
    short s; 
    int i; 
    float f; 
    double d; 

    operator char() const { return c; } 
    operator short() const { return s; } 
    operator int() const { return i; } 
    operator float() const { return f; } 
    operator double() const { return d; } 
    template <typename T> operator T() const { /* invalid conversion */ T t; return t; } 

    Union &operator =(char ac) { c = ac; return *this; } 
    Union &operator =(short as) { s = as; return *this; } 
    Union &operator =(int ai) { i = ai; return *this; } 
    Union &operator =(float af) { f = af; return *this; } 
    Union &operator =(double ad) { d = ad; return *this; } 
    template <typename T> Union &operator =(T at) { /* invalid asignement */ return *this; } 
}; 

それはそれはsome typeとして動作するときに、労働組合の動作を制御することができます:

​​

オペレータのテンプレートバージョンが無効なコンバージョンと一致しています。

関連する問題