0Pricing
C++ Academy · Leçon

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, decltype et void_t pour 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.

Toutes les leçons de ce cours

  1. Spécialisation complète
  2. Spécialisation partielle
  3. SFINAE
  4. Schémas avec enable_if
← Retour à C++ Academy