SFINAE
Activer conditionnellement des fonctions
SFINAE est une leçon C++ Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C++ Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C++ Academy comprend 4 leçons au total.
Que signifie SFINAE ?
SFINAE signifie « Substitution Failure Is Not An Error ». Lorsque le compilateur substitue les arguments du modèle et que le résultat n’est pas valide, le candidat est simplement retiré au lieu de provoquer une erreur bloquante.
- Elle permet de créer des surcharges conditionnelles.
- Elle constitue la base de la sélection à la compilation.
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
isOdd(T n) { return n % 2 != 0; }
int main() {
std::cout << std::boolalpha << isOdd(3) << '\n';
return 0;
}L’échec de substitution en action
Si la substitution d’un type rend la signature d’une fonction invalide, le compilateur l’ignore discrètement et continue d’examiner les autres candidats.
#include <iostream>
template <typename T>
auto sizeOfValue(T t) -> decltype(t.size()) { return t.size(); }
int main() {
std::string s = "hello";
std::cout << sizeOfValue(s) << '\n';
return 0;
}Deux surcharges, une seule gagnante
Définissez deux surcharges dont une seule se substitue correctement pour un type donné. SFINAE écarte celle qui est invalide.
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_integral<T>::value, const char*>::type
name() { return "integral"; }
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, const char*>::type
name() { return "floating"; }
int main() {
std::cout << name<int>() << ' ' << name<double>() << '\n';
return 0;
}decltype et SFINAE sur les expressions
En utilisant decltype sur une expression dans le type de retour, vous faites en sorte qu’une fonction n’existe que si cette expression est valide pour le type concerné.
#include <iostream>
template <typename T>
auto tryDouble(T x) -> decltype(x + x) { return x + x; }
int main() {
std::cout << tryDouble(21) << '\n';
std::cout << tryDouble(2.5) << '\n';
return 0;
}Détecter un membre
Une technique classique de SFINAE permet de détecter si un type possède un membre précis en vérifiant qu’une expression qui l’utilise peut être compilée.
#include <iostream>
#include <type_traits>
#include <vector>
template <typename T>
auto hasSize(int) -> decltype(std::declval<T>().size(), std::true_type{});
template <typename T>
std::false_type hasSize(...);
int main() {
std::cout << std::boolalpha;
std::cout << decltype(hasSize<std::vector<int>>(0))::value << '\n';
std::cout << decltype(hasSize<int>(0))::value << '\n';
return 0;
}La solution de repli ...
Une surcharge qui accepte ... (des arguments variables) possède la priorité la plus faible. Elle sert de solution générale lorsque la surcharge contrainte par SFINAE échoue.
#include <iostream>
template <typename T>
auto pick(int) -> decltype(T{}.value, void()) { std::cout << "has value\n"; }
template <typename T>
void pick(...) { std::cout << "no value\n"; }
struct WithValue { int value = 0; };
int main() {
pick<WithValue>(0);
pick<int>(0);
return 0;
}void_t
std::void_t, introduit en C++17, simplifie la détection : il associe tout type valide à void, de sorte qu’une spécialisation ne s’active que lorsque l’expression vérifiée est valide.
#include <iostream>
#include <type_traits>
#include <vector>
template <typename T, typename = void>
struct HasSize : std::false_type {};
template <typename T>
struct HasSize<T, std::void_t<decltype(std::declval<T>().size())>> : std::true_type {};
int main() {
std::cout << std::boolalpha;
std::cout << HasSize<std::vector<int>>::value << '\n';
std::cout << HasSize<int>::value << '\n';
return 0;
}Pourquoi ne pas provoquer une erreur ?
Sans SFINAE, une substitution invalide provoquerait une erreur de compilation bloquante dont il serait impossible de se remettre. SFINAE la transforme en un simple « n’est pas un candidat ».
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, bool>::type
isNull(T p) { return p == nullptr; }
int main() {
int x = 0;
std::cout << std::boolalpha << isNull(&x) << '\n';
return 0;
}Alternative par sélection de balise
SFINAE peut être difficile à lire. La sélection par balise choisit une implémentation au moyen de petits types balises et de la résolution des surcharges.
#include <iostream>
#include <type_traits>
template <typename T>
void impl(T v, std::true_type) { std::cout << "integral: " << v << '\n'; }
template <typename T>
void impl(T v, std::false_type) { std::cout << "other: " << v << '\n'; }
template <typename T>
void handle(T v) { impl(v, std::is_integral<T>{}); }
int main() {
handle(5);
handle(2.5);
return 0;
}SFINAE et if constexpr
En C++17, if constexpr remplace souvent SFINAE pour effectuer des branchements au sein d’une seule fonction, car les branches inutilisées sont supprimées à la compilation.
#include <iostream>
#include <type_traits>
template <typename T>
void describe(T v) {
if constexpr (std::is_integral<T>::value) {
std::cout << "integral: " << v << '\n';
} else {
std::cout << "other: " << v << '\n';
}
}
int main() {
describe(7);
describe(3.14);
return 0;
}Assembler les éléments
SFINAE est particulièrement utile lorsque vous devez choisir entre plusieurs modèles de fonction distincts selon les propriétés du type, au niveau des surcharges.
#include <iostream>
#include <type_traits>
template <typename T>
typename std::enable_if<std::is_signed<T>::value, T>::type
absValue(T v) { return v < 0 ? -v : v; }
int main() {
std::cout << absValue(-9) << '\n';
return 0;
}Vérification rapide
Vérifiez votre compréhension de SFINAE.
Récapitulatif
Vous avez découvert SFINAE :
- une substitution invalide retire un candidat au lieu de provoquer une erreur
- SFINAE s’utilise avec
enable_if,decltypeetvoid_tpour activer conditionnellement des fonctions - une surcharge
...sert de solution de repli - les alternatives modernes comprennent la sélection par balise et
if constexpr
Vous allez maintenant étudier en détail les motifs classiques utilisant enable_if.
Questions Fréquemment Posées
La leçon « SFINAE » est-elle gratuite ?
Oui — le texte complet de « SFINAE » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C++ Academy, passe à CoddyKit PRO. Le cours C++ Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « SFINAE » ?
Activer conditionnellement des fonctions Tu pratiques C++ Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C++ Academy ?
Aucune expérience préalable n'est requise. C++ Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « SFINAE » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C++ Academy ?
Oui. Chaque leçon C++ Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.