SFINAE (Substitution Failure Is Not An Error) — un mécanisme clé de spécialisation des modèles en C++. Si le choix d'un modèle devient incorrect (par exemple, lorsque le remplacement de type entraîne une erreur au moment du choix du modèle), cela n'est pas une erreur de compilation, mais rend simplement le modèle indisponible pour le choix.
SFINAE est à la base d'outils tels que std::enable_if, divers détecteurs de type (type traits), et met en œuvre le motif de dispatching par tag.
Exemple correct :
#include <type_traits> // Pour les entiers template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type foo(T) { std::cout << "Intégral "; } // Pour les autres template<typename T> typename std::enable_if<!std::is_integral<T>::value, void>::type foo(T) { std::cout << "Pas intégral "; }
Utilisation incorrecte : On peut accidentellement obtenir une ambiguïté d'appel (ambiguous overload) ou provoquer une erreur de compilation si les conditions pour std::enable_if ou la spécialisation ne sont pas correctement décrites.
Peut-on utiliser SFINAE pour choisir entre des surcharges de fonctions normales (non modèles) ?
Réponse : Non, SFINAE ne s'applique qu'aux modèles de fonctions ou de classes. Pour les fonctions ordinaires surchargées, SFINAE ne fonctionne pas. On essaye souvent à tort d'écrire std::enable_if dans les paramètres d'une fonction normale, ce qui ne conduit pas au choix, mais rend simplement la signature indéfinie.
Exemple d'une variante erronée :
void foo(int, typename std::enable_if<true, int>::type* = nullptr) {} // Erreur: pas un modèle
Histoire
Dans la bibliothèque de sérialisation, lors de la mise en œuvre de fonctions modèles avec SFINAE, l'intersection des conditions pour deux spécialisations n'a pas été prise en compte. Résultat : situation d'ambiguous overload et incapacité à compiler le module lors de l'apparition d'un nouveau type de données.
Histoire
Dans le code ancien de la bibliothèque boost::asio, en raison d'une mise en œuvre erronée de SFINAE (via dependent false dans static_assert), des messages d'erreur non lisibles apparaissaient : le compilateur dépliait tous les modèles au lieu de supprimer les options incorrectes. Corrigé par des enable_if séparés au niveau des signatures.
Histoire
Lors du portage de la recherche d'algorithmes par type via SFINAE sur un ancien compilateur (MSVC 2012), l'équipe s'est heurtée au fait que la décomposition des types était effectuée incorrectement, et le modèle choisi acceptait un mauvais type. La vérification a été résolue via type traits avant compilation, éliminant le remplacement dans le modèle lui-même.