54 votes

Comment fonctionne l'implémentation de std :: is_function par Eric Niebler?

La semaine dernière, Eric Niebler a tweeté une implémentation très compacte pour la classe de traits std::is_function :

 #include <type_traits>

template<int I> struct priority_tag : priority_tag<I - 1> {};
template<> struct priority_tag<0> {};

// Function types here:
template<typename T>
char(&is_function_impl_(priority_tag<0>))[1];

// Array types here:
template<typename T, typename = decltype((*(T*)0)[0])>
char(&is_function_impl_(priority_tag<1>))[2];

// Anything that can be returned from a function here (including
// void and reference types):
template<typename T, typename = T(*)()>
char(&is_function_impl_(priority_tag<2>))[3];

// Classes and unions (including abstract types) here:
template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];

template <typename T>
struct is_function
    : std::integral_constant<bool, sizeof(is_function_impl_<T>(priority_tag<3>{})) == 1>
{};
 

Mais comment ça marche?

51voto

ComicSansMS Points 12749

L'idée générale

Plutôt que d'énumérer tous les valides des types de fonction, à l'instar de l' exemple de mise en œuvre sur cpprefereence.comcette application répertorie tous les types qui ne sont pas des fonctions, et ensuite seulement résout true si aucun de ceux-ci est mis en correspondance.

La liste des non-types de fonction se compose de (de bas en haut):

  • Les Classes et les syndicats (y compris les types abstract)
  • Tout ce qui peut être renvoyée par une fonction (y compris void et les types de référence)
  • Types de tableau de

Un type qui ne correspond à aucun de ces types de fonction est un type de fonction. Notez que std::is_function considère explicitement appelable types tels que les lambdas ou des classes avec un appel de fonction de l'opérateur que de ne pas être des fonctions.

is_function_impl_

Nous fournissons une surcharge de l' is_function_impl fonction pour chacun des cas éventuels de non-types de fonction. Les déclarations de fonction peut être un peu difficile à analyser, donc, nous allons casser vers le bas pour l'exemple des classes et des syndicats cas:

template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];

Cette ligne déclare une fonction de modèle is_function_impl_ qui prend un seul argument de type priority_tag<3> et renvoie une référence à un tableau de 4 chars. Comme il est de coutume depuis les anciens jours de C, la syntaxe de déclaration devient horriblement compliquée par la présence de types de tableau.

Ce modèle de fonction prend deux arguments de modèle. La première est juste une contrainte T, mais le second est un pointeur sur un membre de l' T de type int. L' int partie il est question ici n'a pas vraiment d'importance, c'est à dire. ce sera même travail pour Ts qui ne sont pas tous les membres de type int. Ce qu'il fait est qu'il en résultera une erreur de syntaxe pour Ts qui ne sont pas de classe ou de type d'union. Pour les autres types, d'une tentative d'instancier le modèle de fonction va aboutir à une substitution de l'échec.

Des astuces similaires sont utilisés pour l' priority_tag<2> et priority_tag<1> surcharges, qui utilisent leur deuxième modèle arguments pour former des expressions que seuls les compiler pour Ts est valide en fonction des types de retour ou de types de tableau, respectivement. Seulement l' priority_tag<0> de surcharge n'est pas aussi contraignantes deuxième paramètre de modèle et peut donc être instancié avec tout T.

Dans l'ensemble, nous déclarer les quatre différentes surcharges pour is_function_impl_, qui diffèrent par leur argument d'entrée et le type de retour. Chacun d'eux prend une autre priority_tag type en argument et renvoie une référence à un tableau de char de différentes taille unique.

Tag envoi en is_function

Maintenant, lors de l'instanciation is_function, il instancie is_function_impl avec T. Notez que depuis que nous avons fourni quatre différentes surcharges pour cette fonction, résolution de surcharge a lieu ici. Et depuis toutes ces surcharges sont fonction des modèles, ce qui signifie SFINAE a une chance de coup de pied dans.

Donc, pour les fonctions (et fonctionne uniquement) tous les surcharges échoue à l'exception de la plus générale, avec priority_tag<0>. Alors pourquoi ne pas l'instanciation toujours résoudre à la surcharge, si c'est le cas le plus général? Parce que des arguments d'entrée de nos fonctions surchargées.

Notez que priority_tag est construit de telle manière qu' priority_tag<N+1> public hérite priority_tag<N>. Maintenant, depuis is_function_impl est invoquée avec priority_tag<3>, que la surcharge est un meilleur match que les autres pour la résolution de surcharge, de sorte qu'il sera essayé en premier. Uniquement si cela échoue en raison d'une erreur de substitution à la prochaine meilleure correspondance est essayé, ce qui est l' priority_tag<2> de surcharge. Nous continuerons dans cette voie jusqu'à ce que nous soit trouver une surcharge qui peut être instancié ou nous arrivons priority_tag<0>, ce qui n'est pas limité et le sera toujours. Depuis tous les non-types de fonctions sont couverts par la hausse des prio surcharges, ce qui ne peut arriver que pour des types de fonction.

En évaluant le résultat

Nous avons maintenant vérifier la taille du type retourné par l'appel à is_function_impl_ pour évaluer le résultat. Rappelez-vous que chaque surcharge renvoie une référence à un tableau de char de taille différente. Nous pouvons donc utiliser sizeof pour vérifier la surcharge a été sélectionné et seulement définir le résultat de la true si nous avons atteint l' priority_tag<0> de surcharge.

Bugs Connus

Johannes Schaub trouvé un bug dans la mise en œuvre. Un tableau de classe incomplète type seront classés par erreur comme une fonction. C'est parce que l'actuel mécanisme de détection pour les types tableau ne fonctionne pas avec incomplètes types.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X