Comment `void_t travail
J'ai regardé Walter Brown parler à Cppcon14 moderne modèle de programmation (Partie I, Partie II) où il a présenté son void_t
SFINAE technique.
Exemple:
Étant donné une variable simple modèle qui donne void
si tous les arguments de modèle sont bien formés:
template< class ... > using void_t = void;
et suivants trait de caractère qui vérifie l'existence d'une variable membre appelé membre:
template< class , class = void >
struct has_member : std::false_type
{ };
//specialized as has_member< T , void > or discarded (sfinae)
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : std::true_type
{ };
J'ai essayé de comprendre pourquoi et comment cela fonctionne. Donc un petit exemple:
class A {
public:
int member;
};
class B {
};
static_assert( has_member< A >::value , "A" );
static_assert( has_member< B >::value , "B" );
1. has_member< A >
has_member< A , void_t< decltype( A::member ) > >
A::member
existedecltype( A::member )
est bien formévoid_t<>
est valide et évalue àvoid
has_member< A , void >
et, par conséquent, il choisit spécialisées modèlehas_member< T , void >
et évalue àtrue_type
2. has_member< B >
has_member< B , void_t< decltype( B::member ) > >
B::member
n'existe pasdecltype( B::member )
est mal formé et échoue silencieusement (sfinae)has_member< B , expression-sfinae >
donc ce modèle est jeté
- compilateur trouve
has_member< B , class = void >
avec le vide comme argument par défaut has_member< B >
évalue àfalse_type
Questions:
1. Est ma compréhension de ce correct?
2. Walter Brown affirme que l'argument par défaut doit être exactement du même type que celui utilisé dans void_t
pour que cela fonctionne. Pourquoi est-ce? (Je ne vois pas pourquoi ces types doivent correspondre, n'est-ce pas un défaut, le type fait le travail?)
- Ad 2) Imaginez la statique affirmer a été écrit comme:
has_member<A,int>::value
. Ensuite, la spécialisation partielle qui donnehas_member<A,void>
ne peut pas correspondre. Par conséquent, il doit êtrehas_member<A,void>::value
, ou, avec du sucre syntaxique, un argument par défaut de typevoid
. - Merci, je vais modifier cela. Mh, je ne vois pas la nécessité d'en avoir
has_member< T , class = void >
défaut dansvoid
encore. En supposant que ce trait de caractère sera utilisé uniquement avec 1 argument de modèle à tout moment, la valeur par défaut de l'argument pourrait être n'importe quel type? - Question intéressante.
- Notez que Dans cette proposition, open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf, Walter changé
template <class, class = void>
àtemplate <class, class = void_t<>>
. Alors maintenant, nous sommes libres de faire ce que nous voulonsvoid_t
alias implémentation du modèle 🙂
Vous devez vous connecter pour publier un commentaire.
Lorsque vous écrivez
has_member<A>::value
, le compilateur recherche le nomhas_member
et trouve le primaire modèle de classe, qui est, cette déclaration:(Dans l'OP, c'est écrit comme une définition.)
Le modèle de la liste d'arguments
<A>
est comparé au modèle de la liste des paramètres de ce modèle. Depuis la primaire modèle dispose de deux paramètres, mais vous ne fourni à un, les autres paramètre est défini par défaut pour le modèle par défaut de l'argument:void
. C'est comme si vous aviez écrithas_member<A, void>::value
.Maintenant, le modèle de la liste des paramètres est comparée à toutes les spécialisations du modèle
has_member
. Si aucune spécialisation correspond à la définition de la primaire modèle est utilisé comme une solution de repli. Donc la spécialisation partielle est prise en compte:Le compilateur essaie de faire correspondre les arguments de modèle
A, void
avec les modèles définis dans la partielle de spécialisation:T
etvoid_t<..>
un par un. Tout d'abord, le modèle de l'argument de la déduction est effectuée. La spécialisation partielle ci-dessus est encore un modèle avec un modèle de paramètres qui doivent être "rempli" par des arguments.Le premier modèle,
T
, permet au compilateur de déduire le modèle de paramètreT
. C'est une simple déduction, mais envisager un modèle de typeT const&
, où l'on peut encore déduireT
. Pour le modèle de laT
et l'argument de modèleA
, on en déduitT
êtreA
.Dans le deuxième modèle
void_t< decltype( T::member ) >
, le modèle à paramètreT
apparaît dans un contexte où il ne peut pas être déduite à partir de n'importe quel argument de modèle. Il y a deux raisons à cela:L'expression à l'intérieur de
decltype
est explicitement exclus de l'argument de modèle de déduction. Je suppose que c'est parce qu'il peut être arbitrairement complexe.Même si nous avons utilisé un modèle sans
decltype
commevoid_t< T >
, la déduction deT
qui se passe sur l'résolu alias modèle. Qui est, résoudre les alias de modèle, puis essayez d'en déduire le typeT
à partir du modèle obtenu. Le modèle obtenu est cependantvoid
, qui ne dépend pas deT
et, par conséquent, ne nous autorise pas à trouver un type spécifique pourT
. Ceci est similaire à la mathématique du problème d'essayer d'inverser une fonction constante (dans le sens mathématique de ces termes).Argument de modèle déduction est fini(*), maintenant la déduit modèle arguments sont substitués. Cela crée une spécialisation qui ressemble à ceci:
Le type
void_t< decltype( A::member ) > >
maintenant peut être évaluée. Il est bien-formé après la substitution, par conséquent, pas de Substitution Échec se produit. Nous obtenons:Maintenant, nous pouvons comparer le paramètre de modèle de liste de cette spécialisation avec les arguments de modèle livré à l'origine
has_member<A>::value
. Les deux types correspondent exactement, de sorte que cette partielle de la spécialisation choisie.D'autre part, lorsque nous définissons le modèle:
Nous nous retrouvons avec la même spécialisation:
mais notre modèle de liste d'arguments pour
has_member<A>::value
est maintenant<A, int>
. Les arguments ne correspondent pas aux paramètres de la spécialisation, et le premier modèle est choisi comme un recul.(*) La Norme, à mon humble avis prêter à confusion, comprend le processus de substitution et la mise en correspondance des spécifié explicitement arguments de modèle dans le argument de modèle déduction processus. Par exemple (post-N4296) [temp.classe.spec.match]/2:
Mais ce n'est pas juste dire que tous les modèles-les paramètres de la spécialisation partielle doivent être déduites; cela signifie également que la substitution doit réussir, et, comme il semble?) les arguments de modèle correspondent au (substitué) paramètres du modèle de la spécialisation partielle. Notez que je ne suis pas complètement au courant de où la Norme spécifie la comparaison entre les substitué liste d'arguments et de l'argument fourni la liste.
Qu'au-dessus de la spécialisation n'existe que quand il est bien formé, de sorte que lorsque
decltype( T::member )
est valide et non ambiguë.la spécialisation est donc pour
has_member<T , void>
de l'état dans le commentaire.Lorsque vous écrivez
has_member<A>
, il esthas_member<A, void>
en raison de modèle par défaut de l'argument.Et nous avons spécialisation pour
has_member<A, void>
(donc hériter detrue_type
), mais nous n'avons pas de spécialisation pourhas_member<B, void>
(si nous l'utilisons la définition par défaut : hériter defalse_type
)