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