Comment détecter si il y a une variable membre de la classe?
Pour la création de l'algorithme fonction de modèle j'ai besoin de savoir si x ou X (et y ou Y) dans la classe qui est argument de modèle. Il peut, par utile lors de l'utilisation de ma fonction pour MFC CPoint de la classe ou de l'interface GDI+ PointF de la classe ou de certains autres. Tous utilisent différentes x en eux. Ma solution pourrait être réduit au code suivant:
template<int> struct TT {typedef int type;};
template<class P> bool Check_x(P p, typename TT<sizeof(&P::x)>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<sizeof(&P::X)>::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 compile pas dans Visual Studio, lors de la compilation en C++ de GNU. Avec Visual Studio, j'ai pu utiliser le modèle suivant:
template<class P> bool Check_x(P p, typename TT<&P::x==&P::x>::type b = 0) { return true; }
template<class P> bool Check_x(P p, typename TT<&P::X==&P::X>::type b = 0) { return false; }
Mais il n'est pas compilé en C++ de GNU. Est-il solution universelle?
UPD: Structures P1 et P2 sont ici pour exemple. Il y a peut être l'une des classes avec les membres inconnus.
P. S. s'il vous Plaît, ne postez pas de C++11 solutions ici parce qu'ils sont évidents et ne sont pas pertinentes à la question.
- Je ne crois pas que la deuxième façon est la norme (intégrale des expressions constantes ne peuvent pas utiliser l'op== avec des opérandes impliquant des op&). Mais la première ressemble à droite. Ce n'msvc++ dire à ce sujet?
- Regardez le lien à la fin de ma réponse - je pense que explique le problème (à la fois pourquoi les compilateurs de la refuser, et si c'est vraiment autorisé par le C++98 Standard).
- +1: défi Intéressant 🙂
- J'ai écrit une explication approfondie du code correct pour résoudre ce problème, et il est disponible ici: cpptalk.wordpress.com/2009/09/12/... . Désolé de faire cette remarque à deux reprises, mais je pense qu'il appartient, en vertu de la poste principal.
- double possible de Est-il possible d'écrire en C++ modèle pour vérifier la fonction de l'existence?
Vous devez vous connecter pour publier un commentaire.
Une autre façon est de ce type, qui s'appuie sur SFINAE pour les expressions trop. Si le nom résultats de la recherche dans l'ambiguïté, le compilateur va rejeter le modèle
Il est basé sur une brillante idée de quelqu'un sur usenet.
Remarque: HasX vérifie toutes les données ou la fonction membre appelée x, avec un type arbitraire. Le seul but d'introduire le nom du membre est d'avoir une ambiguïté possible pour les états-recherche de nom - type du membre n'est pas important.
integer
membre de bien que, il vérifie toutes les données ou la fonction membre appeléex
, avec un type arbitraire. Le seul but d'introduire le nom du membre est d'avoir une ambiguïté possible pour les états-recherche de nom - le type du membre n'est pas important.ChT<int C::*, &C::x>*
pour vérifier qu'uneint
membre. Peut-être que j'aurais dû choisir un autre type pour les tests afin de la rendre plus claire que seul le nom est important, cependant.T Base::*
àT Derived::*
. Je n'ai pas encore trouvé une solution pour ce problème - toutes les "solutions" ont leurs inconvénients, il me semble.Ici est une solution plus simple que
Johannes Schaub - litb's un. Il nécessite de C++11.
Mise à jour: Un exemple rapide et l'explication sur la façon dont cela fonctionne.
Pour ces types:
nous avons
HasX<A>::value == true
etHasX<B>::value == false
. Nous allons voir pourquoi.Rappelons tout d'abord que
std::false_type
etstd::true_type
ont unstatic constexpr bool
membre nommévalue
qui est fixé àfalse
ettrue
, respectivement. Par conséquent, les deux modèlesHasX
au-dessus de l'héritage de ce membre. (Le premier modèle destd::false_type
et la seconde destd::true_type
.)Commençons simple et, ensuite, de procéder étape par étape jusqu'à ce que nous obtenir le code ci-dessus.
1) point de Départ:
Dans ce cas, il n'y a pas de surprise:
HasX
dérive destd::false_type
et doncHasX<bool, double>::value == false
etHasX<bool, int>::value == false
.2) Défaut
U
:Étant donné que
U
par défautint
,Has<bool>
signifie en faitHasX<bool, int>
et donc,HasX<bool>::value == HasX<bool, int>::value == false
.3) l'Ajout d'une spécialisation:
En général, grâce à la primaire modèle,
HasX<T, U>
dérive destd::false_type
. Cependant, il existe une spécialisation pour lesU = int
qui dérive destd::true_type
. Par conséquent,HasX<bool, double>::value == false
maisHasX<bool, int>::value == true
.Grâce à la valeur par défaut pour
U
,HasX<bool>::value == HasX<bool, int>::value == true
.4)
decltype
et une façon élégante de direint
:Une petite parenthèse ici, mais, s'il vous plaît, pardonnez-moi.
De coeur (ce n'est pas tout à fait correct),
decltype(expression)
donne le type de expression. Par exemple,0
a typeint
ainsi,decltype(0)
signifieint
. De la même façon,1.2
a typedouble
et donc,decltype(1.2)
signifiedouble
.Considérer une fonction avec cette déclaration:
où
foo
est un certain type de classe. Sif
est un objet de typefoo
, puisdecltype(func(f, 0))
signifiechar
(le type retourné parfunc(f, 0)
).Maintenant, l'expression
(1.2, 0)
utilise le (built-in) opérateur virgule qui évalue les deux sous-expressions dans l'ordre (c'est, d'abord1.2
et puis0
), les rejets de la première valeur et les résultats de la deuxième. Par conséquent,est équivalent à
Mettre cela ensemble avec
decltype
donne quedecltype(1.2, 0)
signifieint
. Il n'y a rien de vraiment spécial à propos de1.2
oudouble
ici. Par exemple,true
a typebool
etdecltype(true, 0)
signifieint
ainsi.Que sur un type de classe? Pour instace, ce qui ne
decltype(f, 0)
veux dire? Il est naturel de s'attendre à ce que cela signifie queint
mais il pourrait ne pas être le cas. En effet, il pourrait y avoir une surcharge de l'opérateur virgule similaire à la fonctionfunc
ci-dessus qui prend unfoo
et unint
et renvoie unchar
. Dans ce cas,decltype(foo, 0)
estchar
.Comment pouvons-nous éviter l'utilisation d'une surcharge de l'opérateur virgule? Eh bien, il n'y a aucun moyen de la surcharge de l'opérateur virgule pour un
void
opérande et nous pouvons jeter ce àvoid
. Par conséquent,decltype((void) f, 0)
signifieint
. En effet,(void) f
jettef
defoo
àvoid
qui ne fait rien mais en disant que l'expression doit être considéré comme ayant le typevoid
. Ensuite, l'opérateur virgule est utilisée et((void) f, 0)
résultats dans0
de typeint
. Par conséquent,decltype((void) f, 0)
signifieint
.Est-ce cast-elle vraiment nécessaire? Eh bien, si il n'y a pas de surcharge de l'opérateur virgule en prenant
foo
etint
puis ce n'est pas nécessaire. Nous pouvons toujours inspecter le code source pour voir si il y a un tel opérateur ou non. Toutefois, si cela apparaît dans un modèle etf
a typeV
qui est un paramètre du modèle, il est alors plus claire (ou même impossible de savoir) si une telle surcharge de l'opérateur virgule existe ou pas. Pour être générique nous jette de toute façon.Ligne du bas:
decltype((void) f, 0)
est une façon élégante de direint
.5) SFINAE:
C'est toute une science 😉 OK je suis exagerating mais ce n'est pas très simple non plus. Donc je vais garder l'explication pour le strict minimum.
SFINAE signifie la Substitution de l'Échec n'est Pas Une Erreur. Cela signifie que lorsqu'un paramètre du modèle est remplacé par un type, illégale d'un code C++ peut apparaître, dans certaines conditions,, au lieu de l'abandon de la compilation, le compilateur ignore simplement le code malveillant comme si il n'était pas là. Nous allons voir comment il s'applique à notre cas:
Ici, de nouveau,
decltype((void) T::x, 0)
est une façon élégante de direint
mais avec l'avantage de SFINAE.Quand
T
est substitué avec un type, une défaillance de construire peut apparaître. Par exemple,bool::x
n'est pas valide en C++, donc la substitutionT
avecbool
dansT::x
donne un invalide de construire. En vertu de la SFINAE principe, le compilateur ne rejette pas le code, il ignore simplement des (parties de), il. Plus précisément, comme nous l'avons vuHasX<bool>
signifie en faitHasX<bool, int>
. La spécialisation pourU = int
doit être sélectionné, mais, tandis que de son instanciation, le compilateur trouvebool::x
et ignore le modèle de la spécialisation tout à fait comme si elle n'existait pas.À ce point, le code est essentiellement la même que dans le cas (2) ci-dessus où le principal modèle existe. Par conséquent,
HasX<bool, int>::value == false
.Le même argument utilisé pour
bool
détient pourB
depuisB::x
est une défaillance de construire (B
n'a pas de membrex
). Cependant,A::x
est OK et que le compilateur ne voit pas de problème dans l'instanciation de la spécialisation pourU = int
(ou, plus précisément, pourU = decltype((void) A::x, 0)
). Par conséquent,HasX<A>::value == true
.6) Unnaming
U
:Bien, en regardant le code dans (5) encore une fois, nous voyons que le nom
U
n'est pas utilisé n'importe où, mais dans sa déclaration (typename U
). Nous pouvons alors unname le deuxième argument de modèle et de nous obtenir le code affiché en haut de ce post.void
au lieu deint
, puis de la retirer de l'opérateur virgule trucs dans ledecltype
- ideone.com/QZHfaiJe me suis redirigé ici à partir d'un question qui a été fermé comme un doublon de celui-ci. Je sais que c'est un vieux thread, mais je voulais juste proposer une alternative (plus simple?) la mise en œuvre qui travaille avec C++11. En supposant que nous voulons vérifier si une classe a un membre de la variable appelée
id
:Que c'est. Et voici comment il est utilisé (live exemple):
Choses peuvent être encore plus simple avec un couple de macros:
Qui pourrait être utilisé de cette façon:
declval
à tous, lors de mes testsdecltype(T::id, void())
fonctionne très bienMise à JOUR: j'ai récemment fait un peu plus avec le code que j'ai posté dans ma réponse, donc je suis à jour de ce compte pour des changements et des ajouts.
Ici sont certains de l'utilisation des extraits de:
*Le courage pour tous ce sont plus bas
Vérifier membre
x
dans une classe donnée. Pourrait être var, func, de classe, de l'union, ou enum:Vérifier la fonction de membre de
void x()
:Vérifier la variable de membre
x
:Vérifier membre de la classe
x
:Vérifier membre de l'union
x
:Vérifier membre enum
x
:Vérifier pour toute fonction membre
x
indépendamment de la signature:OU
De détails et de base:
Macros (El Diablo!):
CREATE_MEMBER_CHECK:
CREATE_MEMBER_VAR_CHECK:
CREATE_MEMBER_FUNC_SIG_CHECK:
CREATE_MEMBER_CLASS_CHECK:
CREATE_MEMBER_UNION_CHECK:
CREATE_MEMBER_ENUM_CHECK:
CREATE_MEMBER_FUNC_CHECK:
CREATE_MEMBER_CHECKS:
Coup de pouce.ConceptTraits fournit entre autres quelques macros pour définir des traits de type, comme par exemple
BOOST_TT_EXT_DEFINE_HAS_MEMBER(name)
, qui définit un type de trait, de la forme:Cela donne vrai si T est un type de membre nommé . Notez, cependant, que ce ne sera pas en mesure de détecter le type de référence des membres.
Dans votre cas, il vous suffira d'ajouter dans un fichier d'en-tête
et vérifier comme suit
La technique utilisée est la même que celle expliquée sur certaines des réponses précédentes.
Malheureusement, cette bibliothèque n'est plus maintenu. Maintenant que C++0x ne comprend concept, cette bibliothèque avec SFINAE est un remplacement parfait pour travailler avec la plupart des concepts.
Pourquoi n'utilisez-vous pas la spécialisation comme ceci:
La deuxième réponse (litb) et cela montre comment détecter un membre:
Est-il possible d'écrire un modèle pour vérifier la fonction de l'existence?
Pourquoi ne pas simplement créer des spécialisations de modèle de Check_x ?
Diable, quand je pense à elle. Si vous ne disposez que de deux types, pourquoi avez-vous encore besoin de modèles pour cela?
Sont les fonctions (x, X, y, y) à partir d'une classe de base abstraite, ou pourraient-ils être remaniée de l'être? Si oui, vous pouvez utiliser le SUPERSUBCLASS() macro à partir de Modern C++ Design, avec des idées à partir de la réponse à cette question:
Le type de compilation en fonction de répartition
Nous pouvons obtenir au moment de la compilation:
0 - not_member, 1 - is_object, 2 - is_function
de chaque membre et de classe - objet ou de la fonction: http://ideone.com/Fjm9u5Résultat:
Pour class/struct: