SFINAE (Substitution Failure Is Not An Error) è un meccanismo chiave per la specializzazione dei template in C++. Se nella scelta del template la definizione diventa non valida (ad esempio, quando la sostituzione del tipo provoca un errore nella fase di scelta del template), allora non è un errore di compilazione, ma rende semplicemente il template non disponibile per la scelta.
SFINAE è alla base di strumenti come std::enable_if, vari detector di tipo (type traits), e implementa il pattern tag dispatching.
Esempio corretto:
#include <type_traits> // Per interi template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type foo(T) { std::cout << "Integrale "; } // Per tutti gli altri template<typename T> typename std::enable_if<!std::is_integral<T>::value, void>::type foo(T) { std::cout << "Non integrale "; }
Utilizzo non corretto: Può accadere di ottenere per caso ambiguità di chiamata (ambiguous overload) o di provocare un errore di compilazione se non si descrivono con attenzione le condizioni per std::enable_if o le specializzazioni.
È possibile utilizzare SFINAE per scegliere tra overload di funzioni normali (non template)?
Risposta: No, SFINAE si applica solo ai template di funzioni o classi. Per le normali funzioni sovraccaricate, SFINAE non funziona. Spesso si cerca erroneamente di inserire std::enable_if nei parametri di una funzione normale, il che non porta a una scelta, ma rende semplicemente la firma indefinita.
Esempio di utilizzo errato:
void foo(int, typename std::enable_if<true, int>::type* = nullptr) {} // Errore: non template
Storia
Nella libreria di serializzazione, durante l'implementazione delle funzioni template con SFINAE, non sono stati considerati gli incroci delle condizioni per due specializzazioni. Risultato: situazione di ambiguità nell'overload e impossibilità di compilare il modulo quando è emerso un nuovo tipo di dato.
Storia
Nel vecchio codice della libreria boost::asio, a causa di un'errata implementazione di SFINAE (tramite dependent false in static_assert), apparivano messaggi di errore che non venivano compresi: il compilatore espandeva tutti i template invece di rimuovere le opzioni non valide. È stato risolto tramite enable_if separati a livello di firma.
Storia
Durante il porting della ricerca di algoritmi per tipi tramite SFINAE su un vecchio compilatore (MSVC 2012), il team ha riscontrato che la scomposizione dei tipi veniva eseguita in modo errato e il template selezionato accettava un tipo non valido. La verifica è stata risolta tramite type traits prima della compilazione, eliminando la sostituzione nel template stesso.