SFINAE
Abilitare condizionalmente le funzioni
SFINAE è una lezione C++ Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C++ Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C++ Academy include 4 lezioni in totale.
Che cosa significa SFINAE?
SFINAE è l'acronimo di "Substitution Failure Is Not An Error". Quando il compilatore sostituisce gli argomenti del template e il risultato non è valido, quel candidato viene semplicemente rimosso invece di causare un errore bloccante.
- Consente di definire overload condizionali.
- È alla base della selezione a tempo di compilazione.
#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;
}La sostituzione non riuscita in azione
Se la sostituzione di un tipo rende non valida la firma di una funzione, il compilatore la ignora silenziosamente e continua a esaminare gli altri candidati.
#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;
}Due overload, uno prevale
Si definiscono due overload in modo che, per un determinato tipo, esattamente uno dei due venga sostituito correttamente. La SFINAE scarta quello non valido.
#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 e SFINAE sulle espressioni
L'uso di decltype su un'espressione nel tipo restituito fa sì che una funzione esista solo se quell'espressione è valida per il tipo.
#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;
}Rilevare un membro
Un classico trucco SFINAE rileva se un tipo possiede un determinato membro verificando se un'espressione che lo utilizza viene compilata correttamente.
#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;
}Il fallback con ...
Un overload che accetta ... (argomenti variabili) ha la priorità più bassa. Funge da fallback generale quando l'overload vincolato dalla SFINAE non è valido.
#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 di C++17 semplifica il rilevamento: converte qualsiasi tipo valido in void, quindi una specializzazione si attiva solo quando l'espressione verificata è valida.
#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;
}Perché non generare semplicemente un errore?
Senza SFINAE, una sostituzione non valida produrrebbe un errore di compilazione bloccante dal quale non sarebbe possibile recuperare. La SFINAE la trasforma silenziosamente in un "non candidato".
#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;
}Alternativa con tag dispatch
La SFINAE può essere difficile da leggere. Il tag dispatch seleziona un'implementazione usando piccoli tipi tag e la risoluzione degli overload.
#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 e constexpr if
In C++17, if constexpr spesso sostituisce la SFINAE per diramare il codice all'interno di un'unica funzione, poiché i rami non utilizzati vengono scartati a tempo di compilazione.
#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;
}Mettere insieme i concetti
La SFINAE è particolarmente utile quando occorre scegliere tra template di funzione distinti in base alle proprietà del tipo, a livello di overload.
#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;
}Verifica rapida
Verifichi la Sua comprensione della SFINAE.
Riepilogo
Ha imparato a conoscere la SFINAE:
- una sostituzione non valida rimuove un candidato invece di generare un errore
- si usa con
enable_if,decltypeevoid_tper abilitare condizionalmente le funzioni - un overload
...funge da fallback - le alternative moderne includono il tag dispatch e
if constexpr
Successivamente, si concentrerà in dettaglio sui pattern classici di enable_if.
Domande Frequenti
La lezione «SFINAE» è gratuita?
Sì — il testo completo di «SFINAE» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C++ Academy, passa a CoddyKit PRO. Il corso C++ Academy include 4 lezioni in totale.
Cosa imparerò in «SFINAE»?
Abilitare condizionalmente le funzioni Eserciti C++ Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare C++ Academy?
Non è richiesta alcuna esperienza precedente. C++ Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «SFINAE»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione C++ Academy?
Sì. Ogni lezione C++ Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.