Je veux entrer dans plus de méta-programmation de modèles. Je sais que SFINAE signifie «l'échec de la substitution n'est pas une erreur». Mais quelqu'un peut-il me montrer une bonne utilisation de SFINAE?
c++
templates
metaprogramming
sfinae
rlbond
la source
la source
Réponses:
Voici un exemple (à partir d'ici ):
Quand
IsClassT<int>::Yes
est évalué, 0 ne peut pas être converti enint int::*
car int n'est pas une classe, il ne peut donc pas avoir de pointeur de membre. Si SFINAE n'existait pas, alors vous obtiendrez une erreur de compilation, quelque chose comme «0 ne peut pas être converti en pointeur de membre pour le type non-classe int». Au lieu de cela, il utilise simplement le...
formulaire qui renvoie Two, et évalue donc à false, int n'est pas un type de classe.la source
...
, mais plutôt leint C::*
, que je n'avais jamais vu et que je devais aller chercher. Vous avez trouvé la réponse à ce que c'est et à quoi cela pourrait être utilisé ici: stackoverflow.com/questions/670734/…J'aime utiliser
SFINAE
pour vérifier les conditions booléennes.Cela peut être très utile. Par exemple, je l'ai utilisé pour vérifier si une liste d'initialiseurs collectée à l'aide de la virgule d'opérateur n'est pas plus longue qu'une taille fixe
La liste n'est acceptée que lorsque M est plus petit que N, ce qui signifie que la liste d'initialisation n'a pas trop d'éléments.
La syntaxe
char(*)[C]
signifie: Pointeur vers un tableau avec le type d'élément char et sizeC
. SiC
est faux (0 ici), alors nous obtenons le type invalidechar(*)[0]
, pointeur vers un tableau de taille zéro: SFINAE fait en sorte que le modèle soit alors ignoré.Exprimé avec
boost::enable_if
, ça ressemble à çaEn pratique, je trouve souvent la capacité de vérifier les conditions une capacité utile.
la source
M <= N ? 1 : -1
- être pourrait fonctionner à la place.int foo[0]
. Je ne suis pas surpris qu'il soit supporté, car il permet l'astuce très utile "struct se terminant par un tableau de longueur 0" ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).error C2466: cannot allocate an array of constant size 0
En C ++ 11, les tests SFINAE sont devenus beaucoup plus jolis. Voici quelques exemples d'utilisations courantes:
Choisissez une surcharge de fonction en fonction des traits
En utilisant un idiome de type récepteur, vous pouvez faire des tests assez arbitraires sur un type comme vérifier s'il a un membre et si ce membre est d'un certain type
Voici un exemple en direct: http://ideone.com/dHhyHE J'ai aussi récemment écrit une section entière sur SFINAE et l'envoi de balises dans mon blog (plug sans vergogne mais pertinent) http://metaporky.blogspot.de/2014/08/ part-7-static-dispatch-function.html
Notez que depuis C ++ 14, il y a un std :: void_t qui est essentiellement le même que mon TypeSink ici.
la source
TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>
à un endroit puisTypeSinkT<decltype(&T::bar)>
à un autre? Est-ce aussi le&
nécessairestd::declval<T&>
?TypeSink
, C ++ 17 ontstd::void_t
:)La bibliothèque enable_if de Boost offre une belle interface propre pour utiliser SFINAE. L'un de mes exemples d'utilisation préférés se trouve dans la bibliothèque Boost.Iterator . SFINAE est utilisé pour activer les conversions de type d'itérateur.
la source
C ++ 17 fournira probablement un moyen générique de rechercher des fonctionnalités. Voir N4502 pour plus de détails, mais comme exemple autonome, considérez ce qui suit.
Cette partie est la partie constante, mettez-la dans un en-tête.
L'exemple suivant, tiré de N4502 , montre l'utilisation:
Par rapport aux autres implémentations, celle-ci est assez simple: un ensemble réduit d'outils (
void_t
etdetect
) suffit. En outre, il a été signalé (voir N4502 ) qu'il est nettement plus efficace (temps de compilation et consommation de mémoire du compilateur) que les approches précédentes.Voici un exemple en direct , qui inclut des modifications de portabilité pour GCC pre 5.1.
la source
Voici un autre exemple (tardif) de SFINAE , basé sur la réponse de Greg Rogers :
De cette façon, vous pouvez vérifier la
value
valeur de s pour voir siT
c'est une classe ou non:la source
int C::*
dans votre réponse? Comment peutC::*
être un nom de paramètre?int C::*
est le type d'un pointeur vers uneint
variable membre deC
.Voici un bon article de SFINAE: Une introduction au concept SFINAE de C ++: introspection à la compilation d'un membre de classe .
Résumez-le comme suit:
declval
est un utilitaire qui vous donne une "fausse référence" à un objet d'un type qui ne peut pas être facilement construit.declval
est vraiment pratique pour nos constructions SFINAE.la source
Ici, j'utilise la surcharge de fonction de modèle (pas directement SFINAE) pour déterminer si un pointeur est une fonction ou un pointeur de classe membre: ( Est-il possible de corriger les pointeurs de fonction membre iostream cout / cerr imprimés comme 1 ou true? )
https://godbolt.org/z/c2NmzR
Tirages
Tel que le code est, il pourrait (selon la "bonne" volonté du compilateur) générer un appel à l'exécution à une fonction qui retournera vrai ou faux. Si vous souhaitez forcer l'
is_function_pointer(var)
évaluation au type de compilation (aucun appel de fonction effectué au moment de l'exécution), vous pouvez utiliser l'constexpr
astuce variable:Par le standard C ++, toutes les
constexpr
variables sont garanties d'être évaluées au moment de la compilation ( Calcul de la longueur d'une chaîne C au moment de la compilation. Est-ce vraiment une constexpr? ).la source
Le code suivant utilise SFINAE pour permettre au compilateur de sélectionner une surcharge en fonction du fait qu'un type a une certaine méthode ou non:
Production:
la source