sélectionner un membre d’union en fonction d’un paramètre de modèle

Je traite avec une union en C ++ et j’aimerais disposer d’un modèle de fonction permettant d’accéder au membre de l’union actif en fonction d’un paramètre de modèle.

Le code ressemble à quelque chose comme ça (quelque chose est juste un exemple):

union Union { int16_t i16; int32_t i32; }; enum class ActiveMember { I16 , I32 } template  void doSomething(Union a, const Union b) { selectMemeber(a, M) = selectMember(b, M); // this would be exactly (not equivalent) the same // that aX = bX depending on T. } 

Pour ce faire, je n’ai trouvé que des méthodes malveillantes telles que la spécialisation ou une méthode non homogène d’access et d’atsortingbution.

Je manque quelque chose, et de telles choses devraient être le faire avec une autre approche?

Possibilité 1

au lieu d’utiliser une énumération, vous pouvez utiliser des structures simples pour choisir le membre:

 typedef short int16_t; typedef long int32_t; union Union { int16_t i16; int32_t i32; }; struct ActiveMemberI16 {}; struct ActiveMemberI32 {}; template  void doSomething(Union& a, Union b) { selectMember(a, M()) = selectMember(b, M()); // this would be exactly (not equivalent) the same // that aX = bX 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(a,b); std::cout << a.i16 << std::endl; b.i32 = 3; doSomething(a,b); std::cout << a.i32 << std::endl; return 0; } 

Cela nécessite de définir une méthode struct et une méthode selectMember pour chaque membre de l'union, mais vous pouvez au moins utiliser selectMember pour de nombreuses autres fonctions.

Notez que j'ai transformé les arguments en références, vous pouvez ajuster ceci si cela ne convient pas.

Possibilité 2

En définissant le pointeur d'union sur le type de pointeur souhaité, vous pouvez utiliser une seule fonction selectMember.

 typedef short int16_t; typedef long int32_t; union Union { int16_t i16; int32_t i32; }; template  T& selectMember(Union& u) { return *((T*)&u); } template  void doSomething(Union& a, Union b) { selectMember(a) = selectMember(b); // this would be exactly (not equivalent) the same // that aX = bX depending on T. } int _tmain(int argc, _TCHAR* argv[]) { Union a,b; a.i16 = 0; b.i16 = 1; doSomething(a,b); std::cout << a.i16 << std::endl; b.i32 = 100000; doSomething(a,b); std::cout << a.i32 << std::endl; return 0; } 

Je ne suis pas sûr de savoir pourquoi vous considérez la spécialisation de modèles comme un “mauvais stratagème”, mais il n’existe pas de “statique si” en C ++, donc si vous voulez que votre compilateur différencie le code produit en fonction du résultat d’une expression évaluée au moment de la compilation , vous devez définir différentes versions spécialisées du modèle.

Voici comment vous le définiriez:

 #include  using namespace std; union Union { int16_t int16; int32_t int32; }; enum class ActiveMember { INT16 , INT32 }; // Declare primary template template  void doSomething(Union a, const Union b); // First specialization template <> void doSomething(Union a, const Union b) { a.int16 = b.int16; // Do what you want here... cout << "int16" << endl; } // Second specialization template <> void doSomething(Union a, const Union b) { a.int32 = b.int32; // Do what you want here... cout << "int32" << endl; } 

Et voici comment vous l'utiliseriez.

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

La seule solution à laquelle je peux penser est d’append des opérateurs à l’union:

 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  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  Union &operator =(T at) { /* invalid asignement */ return *this; } }; 

Il vous permet de contrôler le comportement de l’union lorsqu’elle fonctionne comme un type quelconque :

 template  void doSomething(Union a, const Union b) { // call the 'b' conversion operator and the 'a' asignment operator. a = static_cast(b); } int main(int argc, char **argv) { Union a, b; doSomething(a, b); // calls ai = bi doSomething(a, b); // calls ac = bc return 0; } 

La version de modèle des opérateurs correspond aux conversions non valides.