SFINAE (Substitution Failure Is Not An Error) is een sleutelmechanisme voor sjabloonspecialisatie in C++. Als bij de keuze van een sjabloon de definitie ongeldig wordt (bijvoorbeeld wanneer het substitueren van een type leidt tot een fout tijdens de sjabloonselectie), is dat geen compilatiefout, maar maakt het gewoon het sjabloon onbeschikbaar voor selectie.
SFINAE ligt ten grondslag aan instrumenten zoals std::enable_if, verschillende type detectoren (type traits), en implementeert het patroon tag dispatching.
Correct voorbeeld:
#include <type_traits> // Voor gehele getallen template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type foo(T) { std::cout << "Integraal "; } // Voor andere template<typename T> typename std::enable_if<!std::is_integral<T>::value, void>::type foo(T) { std::cout << "Niet integraal "; }
Incorrect gebruik: Je kunt per ongeluk ambiguïteit van aanroep (ambiguous overload) krijgen of een compilatiefout veroorzaken als je de voorwaarden voor std::enable_if of specialisaties niet zorgvuldig beschrijft.
Kan SFINAE worden gebruikt om te kiezen tussen overloads van gewone (niet-sjabloon) functies?
Antwoord: Nee, SFINAE is alleen van toepassing op sjabloonfuncties of -klassen. Voor gewone overbelaste functies werkt SFINAE niet. Vaak wordt ten onrechte geprobeerd std::enable_if in de parameters van een gewone functie te schrijven, wat niet leidt tot selectie, maar gewoon de handtekening onbepaald maakt.
Voorbeeld van een foutieve versie:
void foo(int, typename std::enable_if<true, int>::type* = nullptr) {} // Fout: niet-sjabloon
Verhaal
In de serialisbibliotheek werd bij het implementeren van sjabloonfuncties met SFINAE per ongeluk geen rekening gehouden met de overlap van voorwaarden voor twee specialisaties. Resultaat — situatie van ambiguïteit in overload en onvermogen om de module te compileren bij het verschijnen van een nieuw datatype.
Verhaal
In de oude code van de boost::asio-bibliotheek, bij een foutieve implementatie van SFINAE (via dependent false in static_assert) kwamen foutmeldingen voor die niet leesbaar waren: de compiler ontvouwde alle sjablonen in plaats van de ongeldige opties te verwijderen. Dit werd opgelost door afzonderlijke enable_if op het niveau van de handtekeningen.
Verhaal
Bij het porteren van type-gebaseerde zoekalgoritmes via SFINAE naar een oude compiler (MSVC 2012) stuitte het team op het probleem dat de decompositie van types onjuist werd uitgevoerd, en het gekozen sjabloon een onjuist type accepteerde. De controle werd opgelost via type traits vóór de compilatie, waardoor de substitutie in het sjabloon zelf werd geëlimineerd.