Pour créer une fonction de modèle d’algorithme, je dois savoir si x ou X (et y ou Y) dans la classe est un argument de modèle. Cela peut être utile lorsque j’utilise ma fonction pour la classe MFC CPoint ou la classe GDI + PointF ou d’autres. Ils utilisent tous des x différents. Ma solution pourrait être réduite au code suivant:
template struct TT {typedef int type;}; template bool Check_x(P p, typename TT::type b = 0) { return true; } template bool Check_x(P p, typename TT::type b = 0) { return false; } struct P1 {int x; }; struct P2 {float X; }; // it also could be struct P3 {unknown_type X; }; int main() { P1 p1 = {1}; P2 p2 = {1}; Check_x(p1); // must return true Check_x(p2); // must return false return 0; }
Mais il ne comstack pas dans Visual Studio, lors de la compilation dans GNU C ++. Avec Visual Studio, je pourrais utiliser le modèle suivant:
template bool Check_x(P p, typename TT::type b = 0) { return true; } template bool Check_x(P p, typename TT::type b = 0) { return false; }
Mais il ne comstack pas en GNU C ++. Y a-t-il une solution universelle?
UPD: Les structures P1 et P2 ne sont ici que par exemple. Il pourrait y avoir des classes avec des membres inconnus.
PS S’il vous plaît, ne publiez pas de solutions C ++ 11 ici car elles sont évidentes et ne sont pas pertinentes pour la question.
Une autre façon est celle-ci, qui repose également sur SFINAE pour les expressions . Si la recherche de nom entraîne une ambiguïté, le compilateur rejettera le modèle
template struct HasX { struct Fallback { int x; }; // introduce member name "x" struct Derived : T, Fallback { }; template struct ChT; template static char (&f(ChT*))[1]; template static char (&f(...))[2]; static bool const value = sizeof(f(0)) == 2; }; struct A { int x; }; struct B { int X; }; int main() { std::cout << HasX::value << std::endl; // 1 std::cout << HasX::value << std::endl; // 0 }
C'est basé sur une idée géniale de quelqu'un sur Usenet.
Remarque: HasX recherche les données ou les membres de fonction appelés x, avec un type arbitraire. Le seul but du nom de membre est d’avoir une ambiguïté possible pour la recherche de nom de membre - le type du membre n’est pas important.
Voici une solution plus simple que celle de Johannes Schaub – litb . Il nécessite C ++ 11.
#include template struct HasX : std::false_type { }; template struct HasX : std::true_type { };
Mise à jour : Un exemple rapide et l’explication de la façon dont cela fonctionne.
Pour ces types:
struct A { int x; }; struct B { int y; };
nous avons HasX::value == true
et HasX::value == false
. Voyons pourquoi.
std::false_type
abord que std::false_type
et std::true_type
ont un membre static constexpr bool
nommé value
qui est défini sur false
et true
, respectivement. Par conséquent, les deux modèles HasX
ci-dessus héritent de ce membre. (Le premier modèle de std::false_type
et le second de std::true_type
.)
Commençons simple et procédons pas à pas jusqu’à ce que nous arrivions au code ci-dessus.
1) Point de départ:
template struct HasX : std::false_type { };
Dans ce cas, il n’y a pas de surprise: HasX
dérive de std::false_type
et donc HasX
et HasX
.
2) U
défaut:
// Primary template template struct HasX : std::false_type { };
Etant donné que U
est HasX
défaut sur int
, Has
signifie-t-il HasX
et donc, HasX
.
3) Ajouter une spécialisation:
// Primary template template struct HasX : std::false_type { }; // Specialization for U = int template struct HasX : std::true_type { };
En général, grâce au template primaire, HasX
dérive de std::false_type
. Cependant, il existe une spécialisation pour U = int
qui dérive de std::true_type
. Par conséquent, HasX
mais HasX
.
Grâce à la valeur par défaut pour U
, HasX
.
4) decltype
et une façon decltype
de dire int
:
Une petite digression ici, mais, s’il vous plaît, supportez-moi.
Fondamentalement (ce n’est pas tout à fait correct), decltype(expression)
donne le type d’ expression . Par exemple, 0
a le type int
donc, decltype(0)
signifie int
. De manière analogue, 1.2
a le type double
et ainsi, decltype(1.2)
signifie double
.
Considérons une fonction avec cette déclaration:
char func(foo, int);
où foo
est un type de classe. Si f
est un object de type foo
, alors decltype(func(f, 0))
signifie char
(le type renvoyé par func(f, 0)
).
Maintenant, l’expression (1.2, 0)
utilise l’opérateur de virgule (intégré) qui évalue les deux sous-expressions dans l’ordre (c’est-à-dire d’abord 1.2
puis 0
), supprime la première valeur et en génère la seconde. Par conséquent,
int x = (1.2, 0);
est équivalent à
int x = 0;
Le mettre avec decltype
que decltype(1.2, 0)
signifie int
. Il n’y a rien de vraiment spécial à propos de 1.2
ou de double
ici. Par exemple, true
a le type bool
et decltype(true, 0)
signifie également int
.
Qu’en est-il d’un type de classe? Pour instace, que signifie decltype(f, 0)
? Il est naturel de s’attendre à ce que cela signifie toujours int
mais ce ne sera peut-être pas le cas. En effet, il pourrait y avoir une surcharge pour l’opérateur de virgule similaire à la fonction func
ci-dessus qui prend un foo
et un int
et renvoie un caractère. Dans ce cas, decltype(foo, 0)
est char
.
Comment éviter l’utilisation d’une surcharge pour l’opérateur virgule? Eh bien, il n’y a aucun moyen de surcharger l’opérateur de virgule pour un opérande void
et nous pouvons tout jeter à rien. Par conséquent, decltype((void) f, 0)
signifie int
. En effet, (void) f
f
de foo
to void
ce qui ne fait rien d’autre que de dire que l’expression doit être considérée comme ayant un type void
. Ensuite, la virgule de l’opérateur intégré est utilisée et ((void) f, 0)
donne comme résultat 0
qui a le type int
. Par conséquent, decltype((void) f, 0)
signifie int
.
Ce casting est-il vraiment nécessaire? Eh bien, s’il n’y a pas de surcharge pour l’opérateur virgule prenant foo
et int
ce n’est pas nécessaire. Nous pouvons toujours inspecter le code source pour voir si un tel opérateur existe ou non. Cependant, si cela apparaît dans un modèle et que f
a le type V
qui est un paramètre de modèle, alors il n’est plus clair (ou même impossible de savoir) si cette surcharge existe pour l’opérateur de virgule. Pour être générique, on lance quand même.
Bottom line: decltype((void) f, 0)
est une façon decltype((void) f, 0)
de dire int
.
5) SFINAE:
C’est une science entière 😉 OK, j’exagère mais ce n’est pas très simple non plus. Je vais donc garder l’explication au ssortingct minimum.
SFINAE signifie Échec de la substitution n’est pas une erreur. Cela signifie que lorsqu’un paramètre de modèle est remplacé par un type, un code C ++ illégal peut apparaître mais, dans certaines circonstances , au lieu d’abandonner la compilation, le compilateur ignore simplement le code incriminé comme s’il n’y était pas. Voyons comment cela s’applique à notre cas:
// Primary template template struct HasX : std::false_type { }; // Specialization for U = int template struct HasX : std::true_type { };
Ici encore, decltype((void) T::x, 0)
est une façon élégante de dire int
mais avec l’avantage de SFINAE.
Lorsque T
est remplacé par un type, une construction non valide peut apparaître. Par exemple, bool::x
n’est pas valide en C ++, par conséquent, remplacer T
par bool
dans T::x
donne une construction invalide. Sous le principe SFINAE, le compilateur ne rejette pas le code, il l’ignore simplement (en partie). Plus précisément, comme nous l’avons vu, HasX
signifie en fait HasX
. La spécialisation pour U = int
doit être sélectionnée mais, lors de l’instanciation, le compilateur trouve bool::x
et ignore la spécialisation du template comme si elle n’existait pas.
À ce stade, le code est essentiellement le même que dans le cas (2) ci-dessus, où seul le modèle principal existe. Par conséquent, HasX
.
Le même argument utilisé pour bool
est valable pour B
puisque B::x
est une construction invalide ( B
n’a pas de membre x
). Cependant, A::x
est OK et le compilateur ne voit aucun problème à instancier la spécialisation pour U = int
(ou, plus précisément, pour U = decltype((void) A::x, 0)
). Par conséquent, HasX::value == true
.
6) Déblocage U
:
Eh bien, en regardant à nouveau le code dans (5), nous voyons que le nom U
n’est utilisé nulle part ailleurs que dans sa déclaration ( typename U
). Nous pouvons alors nommer le second argument du modèle et nous obtenons le code indiqué en haut de ce post.
J’ai été redirigé ici à partir d’une question qui a été fermée en deux. Je sais que c’est un ancien thread, mais je voulais juste suggérer une implémentation alternative (plus simple?) Qui fonctionne avec C ++ 11. En supposant que l’on veuille vérifier si une certaine classe a une variable membre appelée id
:
#include template struct has_id : std::false_type { }; template struct has_id().id, void())> : std::true_type { };
C’est tout. Et voici comment il serait utilisé ( exemple en direct ):
#include using namespace std; struct X { int id; }; struct Y { int foo; }; int main() { cout << boolalpha; cout << has_id::value << endl; cout << has_id::value << endl; }
Les choses peuvent être simplifiées avec quelques macros:
#define DEFINE_MEMBER_CHECKER(member) \ template \ struct has_ ## member : false_type { }; \ template \ struct has_ ## member().member), void>::value, \ bool \ >::type \ > : true_type { }; #define HAS_MEMBER(C, member) \ has_ ## member::value
Qui pourrait être utilisé de cette façon:
using namespace std; struct X { int id; }; struct Y { int foo; }; DEFINE_MEMBER_CHECKER(foo) int main() { cout << boolalpha; cout << HAS_MEMBER(X, foo) << endl; cout << HAS_MEMBER(Y, foo) << endl; }
MISE À JOUR: J’ai récemment fait un peu plus avec le code que j’ai posté dans ma réponse originale, alors je le mets à jour pour tenir compte des modifications / ajouts.
Voici quelques extraits d’utilisation: * Les entrailles pour tout cela sont plus loin
Vérifiez le membre x
dans une classe donnée. Peut être var, func, class, union ou enum:
CREATE_MEMBER_CHECK(x); bool has_x = has_member_x::value;
Vérifiez la fonction membre void x()
:
//Func signature MUST have T as template variable here... simpler this way :\ CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x); bool has_func_sig_void__x = has_member_func_void__x::value;
Recherchez la variable membre x
:
CREATE_MEMBER_VAR_CHECK(x); bool has_var_x = has_member_var_x::value;
Recherchez la classe de membre x
:
CREATE_MEMBER_CLASS_CHECK(x); bool has_class_x = has_member_class_x::value;
Vérifier l’union membre x
:
CREATE_MEMBER_UNION_CHECK(x); bool has_union_x = has_member_union_x::value;
Vérifier le membre enum x
:
CREATE_MEMBER_ENUM_CHECK(x); bool has_enum_x = has_member_enum_x::value;
Vérifiez toute fonction membre x
indépendamment de la signature:
CREATE_MEMBER_CHECK(x); CREATE_MEMBER_VAR_CHECK(x); CREATE_MEMBER_CLASS_CHECK(x); CREATE_MEMBER_UNION_CHECK(x); CREATE_MEMBER_ENUM_CHECK(x); CREATE_MEMBER_FUNC_CHECK(x); bool has_any_func_x = has_member_func_x::value;
OU
CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above. bool has_any_func_x = has_member_func_x::value;
Détails et kernel:
/* - Multiple inheritance forces ambiguity of member names. - SFINAE is used to make aliases to member names. - Expression SFINAE is used in just one generic has_member that can accept any alias we pass it. */ template struct ambiguate : public Args... {}; template struct got_type : std::false_type {}; template struct got_type : std::true_type { typedef A type; }; template struct sig_check : std::true_type {}; template struct has_member { template static char ((&f(decltype(&C::value))))[1]; template static char ((&f(...)))[2]; //Make sure the member name is consistently spelled the same. static_assert( (sizeof(f(0)) == 1) , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified." ); static bool const value = sizeof(f(0)) == 2; };
Macros (El Diablo!):
CREATE_MEMBER_CHECK:
//Check for any member with given name, whether var, func, class, union, enum. #define CREATE_MEMBER_CHECK(member) \ \ template \ struct Alias_##member; \ \ template \ struct Alias_##member < \ T, std::integral_constant::value> \ > { static const decltype(&T::member) value; }; \ \ struct AmbiguitySeed_##member { char member; }; \ \ template \ struct has_member_##member { \ static const bool value \ = has_member< \ Alias_##member> \ , Alias_##member \ >::value \ ; \ }
CREATE_MEMBER_VAR_CHECK:
//Check for member variable with given name. #define CREATE_MEMBER_VAR_CHECK(var_name) \ \ template \ struct has_member_var_##var_name : std::false_type {}; \ \ template \ struct has_member_var_##var_name< \ T \ , std::integral_constant< \ bool \ , !std::is_member_function_pointer::value \ > \ > : std::true_type {}
CREATE_MEMBER_FUNC_SIG_CHECK:
//Check for member function with given name AND signature. #define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \ \ template \ struct has_member_func_##templ_postfix : std::false_type {}; \ \ template \ struct has_member_func_##templ_postfix< \ T, std::integral_constant< \ bool \ , sig_check::value \ > \ > : std::true_type {}
CREATE_MEMBER_CLASS_CHECK:
//Check for member class with given name. #define CREATE_MEMBER_CLASS_CHECK(class_name) \ \ template \ struct has_member_class_##class_name : std::false_type {}; \ \ template \ struct has_member_class_##class_name< \ T \ , std::integral_constant< \ bool \ , std::is_class< \ typename got_type ::type \ >::value \ > \ > : std::true_type {}
CREATE_MEMBER_UNION_CHECK:
//Check for member union with given name. #define CREATE_MEMBER_UNION_CHECK(union_name) \ \ template \ struct has_member_union_##union_name : std::false_type {}; \ \ template \ struct has_member_union_##union_name< \ T \ , std::integral_constant< \ bool \ , std::is_union< \ typename got_type ::type \ >::value \ > \ > : std::true_type {}
CREATE_MEMBER_ENUM_CHECK:
//Check for member enum with given name. #define CREATE_MEMBER_ENUM_CHECK(enum_name) \ \ template \ struct has_member_enum_##enum_name : std::false_type {}; \ \ template \ struct has_member_enum_##enum_name< \ T \ , std::integral_constant< \ bool \ , std::is_enum< \ typename got_type ::type \ >::value \ > \ > : std::true_type {}
CREATE_MEMBER_FUNC_CHECK:
//Check for function with given name, any signature. #define CREATE_MEMBER_FUNC_CHECK(func) \ template \ struct has_member_func_##func { \ static const bool value \ = has_member_##func::value \ && !has_member_var_##func ::value \ && !has_member_class_##func ::value \ && !has_member_union_##func ::value \ && !has_member_enum_##func ::value \ ; \ }
CREATE_MEMBER_CHECKS:
//Create all the checks for one member. Does NOT include func sig checks. #define CREATE_MEMBER_CHECKS(member) \ CREATE_MEMBER_CHECK(member); \ CREATE_MEMBER_VAR_CHECK(member); \ CREATE_MEMBER_CLASS_CHECK(member); \ CREATE_MEMBER_UNION_CHECK(member); \ CREATE_MEMBER_ENUM_CHECK(member); \ CREATE_MEMBER_FUNC_CHECK(member)
Boost.ConceptTraits fournit entre autres des macros pour définir des traits de caractères, comme par exemple BOOST_TT_EXT_DEFINE_HAS_MEMBER(name)
, qui définit un trait de type de la forme:
has_member_##name
Cela donne vrai si T a un type de membre nommé. Notez cependant que cela ne détectera pas les membres de type référence.
Dans votre cas, il suffira d’append un fichier d’en-tête
BOOST_TT_EXT_DEFINE_HAS_MEMBER_TYPE(x)
et vérifiez comme suit
BOOST_STATIC_ASSERT(has_member_x::value);
La technique utilisée est la même que celle expliquée dans certaines des réponses précédentes.
Malheureusement, cette bibliothèque n’est plus maintenue. Maintenant que C ++ 0x n’inclut pas le concept, cette bibliothèque, associée à SFINAE, est un remplacement parfait pour travailler avec la plupart des concepts.
Pourquoi n’utilisez-vous pas une spécialisation comme celle-ci:
struct P1 {int x; }; struct P2 {int X; }; template bool Check_x(P p) { return true; } template<> bool Check_x(P2 p) { return false; }
La deuxième réponse (litb) à ceci montre comment détecter un membre:
Est-il possible d’écrire un modèle pour vérifier l’existence d’une fonction?
Pourquoi ne créez-vous pas simplement des spécialisations de modèle de Check_x?
template<> bool Check_x(P1 p) { return true; } template<> bool Check_x(P2 p) { return false; }
Heck, quand j’y pense. Si vous ne disposez que de deux types, pourquoi avez-vous même besoin de modèles pour cela?
Les fonctions (x, X, y, Y) sont-elles issues d’une classe de base abstraite ou peuvent-elles être refactorisées pour l’être? Si oui, vous pouvez utiliser la macro SUPERSUBCLASS () de Modern C ++ Design, ainsi que des idées provenant de la réponse à cette question:
Expédition basée sur le type de compilation
Nous pouvons obtenir au moment de la compilation: 0 - not_member, 1 - is_object, 2 - is_function
pour chaque classe et membre requirejs – object ou fonction: http://ideone.com/Fjm9u5
#include #include #define IS_MEMBER(T1, M) \ struct { \ struct verystrangename1 { bool M; }; \ template struct verystrangename2 : verystrangename1, public T { }; \ \ enum return_t { not_member, is_object, is_function }; \ template ::M)> constexpr return_t what_member() { return not_member; } \ template typename std::enable_if::value, return_t>::type constexpr what_member() { return is_object; } \ template typename std::enable_if::value, return_t>::type constexpr what_member() { return is_function; } \ constexpr operator return_t() { return what_member(); } \ } struct t { int aaa; float bbb; void func() {} }; // Can't be in function IS_MEMBER(t, aaa) is_aaa_member_of_t; IS_MEMBER(t, ccc) is_ccc_member_of_t; IS_MEMBER(t, func) is_func_member_of_t; // known at comstack time enum { const_is_aaa_member_of_t = (int)is_aaa_member_of_t }; static constexpr int const_is_func_member_of_t = is_func_member_of_t; int main() { std::cout << std::boolalpha << "0 - not_member, 1 - is_object, 2 - is_function \n\n" << "is aaa member of t = " << is_aaa_member_of_t << std::endl << "is ccc member of t = " << is_ccc_member_of_t << std::endl << "is func member of t = " << is_func_member_of_t << std::endl << std::endl; return 0; }
Résultat:
0 - not_member, 1 - is_object, 2 - is_function is aaa member of t = 1 is ccc member of t = 0 is func member of t = 2
Pour class / struct:
struct t { int aaa; float bbb; void func() {} };