Utilisation de enable_if pour ne faire correspondre que les classes qui ont un certain membre de données statique et qui n’ont que des valeurs spécifiques

Je souhaite spécialiser une fonction pour un sous-ensemble de classes qui: ont une certaine variable de membre de données statique, et cette variable n’a que certaines valeurs possibles

Le code ci-dessous illustre l’intention, mais il ne comstack que si je commente les lignes relatives aux classes B dans main . En effet, le code n’est pas un membre des classes Bx , mais la condition enable_if est valide si l’argument de modèle contient des variables de membre de code . Comment devrais-je le modifier?

Malheureusement, je travaille avec de très vieux compilateurs, donc pas de support pour C ++ 11: je comstack avec l’option -std=c++03 .

Merci

 #include  #include  #include  #include  #include  using std::cout; using namespace boost; using namespace boost::mpl; template  struct A1 { static const int code = N; }; template  struct A2 { static const int code = N; }; // ... other classes with static data member 'code' template  struct AN { static const int code = N; }; struct B1{}; struct B2{}; // ... other classes potentially passd as argument to the foo function struct BN{}; template  struct Condition : or_<bool_, bool_ > {}; template  typename enable_if<not_<Condition >, void>::type foo(const T& arg) { cout << "This class does not have a static member code or its value is not 1 or 2\n"; } template  typename enable_if<Condition, void>::type foo(const T& arg) { cout << "This class has a static member code and its value is " << T::code << "\n"; } int main() { foo(A1()); // this should match the 1st version of foo foo(A2()); // this should match the 2nd version of foo foo(AN()); // this should match the 2nd version of foo foo(B1()); // this should match the 1st version of foo foo(BN()); // this should match the 1st version of foo } 

Le vrai problème est que vous avez besoin d’une solution C ++ 03. Vous pouvez donc utiliser SFINAE, mais pas toutes les améliorations de langage disponibles à partir de C ++ 11.

Quoi qu’il en soit, je vous propose une solution compliquée comme la vôtre (peut-être plus) mais totalement libérée du boost.

Si vous définissez un wrapper bool sortingvial (qui peut remplacer approximativement le std::true_type ++ std::true_type et le std::false_type C ++ 11)

 template  struct bool_wrapper { static const bool value = B; }; 

vous pouvez définir votre condition comme suit

 template  > struct cond : public bool_wrapper { }; template  struct cond > : public bool_wrapper { }; 

et si vous définissez un enable_if type enable_if (identique à C ++ 11 std::enable_if )

 template  struct enable_if { }; template  struct enable_if { typedef T type; }; 

vous pouvez SFINAE activer / désactiver vos fonctions foo()

 template  typename enable_if::value>::type foo (T const & arg) { std::cout << "no static member code or value not 1 and not 2\n"; } template  typename enable_if::value>::type foo (T const & arg) { std::cout << "static member code and its value is " << T::code << "\n"; } 

Voici un exemple complet de travail en C ++ 98

 #include  template  struct A1 { static const int code = N; }; template  struct A2 { static const int code = N; }; // ... template  struct AN { static const int code = N; }; struct B1{}; struct B2{}; // ... struct BN{}; template  struct bool_wrapper { static const bool value = B; }; template  > struct cond : public bool_wrapper { }; template  struct cond > : public bool_wrapper { }; template  struct enable_if { }; template  struct enable_if { typedef T type; }; template  typename enable_if::value>::type foo (T const & arg) { std::cout << "no static member code or value not 1 and not 2\n"; } template  typename enable_if::value>::type foo (T const & arg) { std::cout << "static member code and its value is " << T::code << "\n"; } int main () { foo(A1<0>()); // match the 1st version of foo foo(A2<1>()); // match the 2nd version of foo foo(AN<2>()); // match the 2nd version of foo foo(B1()); // match the 1st version of foo foo(BN()); // match the 1st version of foo } 

Je ne connais pas les classes de boost que vous utilisez mais je suppose que vous pouvez modifier votre code (pour qu'il fonctionne presque comme ma solution sans boost), comme suit

 template  > struct Condition : public bool_ { }; template  struct Condition > : public bool_ { }; 

-- MODIFIER --

Le PO demande

Je ne comprends pas comment cela fonctionne pour le cas A1 <0>. La spécialisation de Condition doit être la correspondance préférée, le deuxième argument étant étendu à bool_. Cette classe hérite de bool_, elle doit donc choisir les mauvaises versions de foo. Cependant cela fonctionne. Comment est-ce possible?

Eh bien ... lorsque vous écrivez foo(A1<0>()) , le compilateur doit comprendre si cond>::value est true ou false pour activer la première version de foo() ou la seconde.

Le compilateur doit donc implémenter cond> . Mais il n’existe pas de classe de modèles cond qui reçoive uniquement un nom de type. Quoi qu'il en soit, le compilateur trouve que

 template  > struct cond; 

correspond à l'aide de la valeur par défaut pour le deuxième paramètre de modèle.

Il n'y a pas d'alternative, donc il n'y a pas d'ambiguïté, donc cond< A<1> > cond< A<1>, bool_wrapper > cond< A<1> > devient cond< A<1>, bool_wrapper >

Maintenant, le compilateur doit choisir entre la version principale de cond (celle qui hérite de bool_wrapper ) et la spécialisation (celle qui hérite de bool_wrapper ).

cond< A<1>, bool_wrapper > correspond sûrement à la version principale, mais correspond également à la spécialisation? Si correspond également à la spécialisation, le compilateur doit préférer la spécialisation.

Il faut donc voir si cond< A<0>, bool_wrapper > correspond à la spécialisation.

En utilisant A<0> comme T , nous obtenons que la spécialisation devienne

 cond< A<0>, bool_wrapper<(1 == A<0>::code) || (2 == A<0>::code)> > 

C'est

 cond< A<0>, bool_wrapper<(1 == 0) || (2 == 0)> > 

C'est

 cond< A<0>, bool_wrapper > 

C'est

 cond< A<0>, bool_wrapper > 

et cela ne correspond pas à cond< A<0>, bool_wrapper > .

Donc cond< A<0> > cond< A<0>, bool_wrapper > cond< A<0> > , c'est-à-dire cond< A<0>, bool_wrapper > , correspond uniquement à la version principale de cond , donc bool_wrapper de bool_wrapper .

Nous pouvons maintenant examiner cond< A<1> > .

En ce qui concerne cond< A<0> > cond , le seul modèle cond correspondant à cond< A<1> > cond est cond avec le deuxième typename avec la valeur par défaut.

Donc cond< A<1> > cond< A<1>, bool_wrapper > cond< A<1> > est cond< A<1>, bool_wrapper > .

Mais cond< A<1>, bool_wrapper > correspond uniquement à la version principale de cond ou également à la spécialisation?

Nous pouvons voir qu'en utilisant A<1> tant que T , nous avons que la spécialisation devient

 cond< A<1>, bool_wrapper<(1 == A<1>::code) || (2 == A<1>::code)> > 

C'est

 cond< A<1>, bool_wrapper<(1 == 1) || (2 == 1)> > 

C'est

 cond< A<1>, bool_wrapper > 

C'est

 cond< A<1>, bool_wrapper > 

et cela correspond à cond< A<1>, bool_wrapper > .

Donc, pour cond< A<1> > cond< A<1>, bool_wrapper cond< A<1> > , AKA cond< A<1>, bool_wrapper , les deux versions de cond correspondent, le compilateur doit donc choisir la spécialisation, donc cond< A<1> > cond cond< A<1> > hériter de bool_wrapper .

Sur la suggestion de J. Zwinck, je l’ai fait fonctionner.

Placer les deux conditions dans une classe and_ template ne fonctionne pas pour les mêmes raisons que celles décrites ci-dessus. Cependant, je peux mettre la première condition (la classe a un code variable membre) dans un enable_if , puis la deuxième condition (le code a des valeurs spécifiques) à l’intérieur de la classe (voir l’exemple ci-dessous).

Si est un peu compliqué cependant. Si quelqu’un peut suggérer une solution plus élégante, je l’accepterai.

 #include  BOOST_TTI_HAS_STATIC_MEMBER_DATA(code) template  struct Condition : false_type {};; template  struct Condition::value> >::type> { typedef or_, bool_ > type; const static bool value = type::value; };