7

私は C++ で共用体を扱っています。テンプレート パラメーターに応じてアクティブな共用体メンバーにアクセスする関数テンプレートが必要です。

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

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.
}

To accomplish this I only found bad hacks like specialization, or a not homogeneous way to access and assign.

I'm missing something, and such things should be do it with other approach?

4

3 に答える 3

7

可能性 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;
}
于 2013-01-17T09:39:37.677 に答える
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;
}
于 2013-01-17T09:22:03.943 に答える
1

私が考えることができる唯一の解決策は、演算子をユニオンに追加することです:

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

ユニオンが何らかのタイプとして機能する場合、ユニオンの動作を制御できます。

template <typename T>
void doSomething(Union a, const Union b)
{
    // call the 'b' conversion operator and the 'a' asignment operator.
    a = static_cast<T>(b);
}

int main(int argc, char **argv)
{
    Union a, b;

    doSomething<int>(a, b);  // calls a.i = b.i
    doSomething<char>(a, b); // calls a.c = b.c

    return 0;
}

演算子のテンプレート バージョンは、無効な変換と一致します。

于 2013-01-17T09:52:00.983 に答える