J'utilise beaucoup la fonction SFINAE dans un projet et je ne sais pas s'il existe des différences entre les deux approches suivantes (autres que le style):
#include <cstdlib>
#include <type_traits>
#include <iostream>
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
std::cout << "method 1" << std::endl;
}
template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
std::cout << "method 2" << std::endl;
}
int main()
{
foo<int>();
foo<double>();
std::cout << "Done...";
std::getchar();
return EXIT_SUCCESS;
}
La sortie du programme est comme prévu:
method 1
method 2
Done...
J'ai vu la méthode 2 utilisée plus souvent dans stackoverflow, mais je préfère la méthode 1.
Y a-t-il des circonstances où ces deux approches diffèrent?
Réponses:
Suggestion: préférez la méthode 2.
Les deux méthodes fonctionnent avec des fonctions uniques. Le problème se pose lorsque vous avez plus d'une fonction, avec la même signature, et que vous ne souhaitez activer qu'une seule fonction de l'ensemble.
Supposons que vous voulez activer
foo()
, version 1, quandbar<T>()
(faire semblant que c'est uneconstexpr
fonction) esttrue
, etfoo()
, la version 2, quandbar<T>()
estfalse
.Avec
vous obtenez une erreur de compilation car vous avez une ambiguïté: deux
foo()
fonctions avec la même signature (un paramètre de modèle par défaut ne change pas la signature).Mais la solution suivante
fonctionne, car SFINAE modifie la signature des fonctions.
Observation indépendante: il existe également une troisième méthode: activer / désactiver le type de retour (sauf pour les constructeurs de classe / struct, évidemment)
En tant que méthode 2, la méthode 3 est compatible avec la sélection de fonctions alternatives avec la même signature.
la source
foo()
fonction reste disponible lorsque vous l'appelez avec un deuxième paramètre de modèle explicite (l'foo<double, double>();
appel). Et si rester disponible, il y a une ambiguïté avec l'autre version. Avec la méthode 2, SFINAE active / désactive le deuxième argument, pas le paramètre par défaut. Vous ne pouvez donc pas l'appeler en expliquant le paramètre car il y a un échec de substitution qui ne permet pas un deuxième paramètre. Donc la version n'est pas disponible, donc pas d'ambiguïtéauto foo() -> std::enable_if_t<...>
est souvent utile pour éviter de masquer la signature de fonction et pour permettre l'utilisation des arguments de fonction.En plus de la réponse de max66 , une autre raison de préférer la méthode 2 est qu'avec la méthode 1, vous pouvez (accidentellement) passer un paramètre de type explicite comme deuxième argument de modèle et vaincre complètement le mécanisme SFINAE. Cela peut se produire comme une faute de frappe, une erreur de copier / coller ou comme une erreur dans un mécanisme de modèle plus grand.
Démo en direct ici
la source