SFINAE (Substitution Failure Is Not An Error) — kluczowy mechanizm specjalizacji szablonów w C++. Jeśli podczas wyboru szablonu definicja staje się niepoprawna (na przykład, gdy podstawienie typu prowadzi do błędu na etapie wyboru szablonu), to nie jest to błąd kompilacji, a po prostu czyni szablon niedostępnym do wyboru.
SFINAE leży u podstaw narzędzi typu std::enable_if, różnych detektorów typu (type traits), realizuje wzorzec tag dispatching.
Poprawny przykład:
#include <type_traits> // Dla całkowitych template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type foo(T) { std::cout << "Całkowity "; } // Dla pozostałych template<typename T> typename std::enable_if<!std::is_integral<T>::value, void>::type foo(T) { std::cout << "Nie całkowity "; }
Niepoprawne użycie: Można przypadkowo uzyskać niejednoznaczność wywołania (ambiguous overload) lub spowodować błąd kompilacji, jeśli nieostrożnie opisano warunki dla std::enable_if lub specjalizacji.
Czy można użyć SFINAE do wyboru między przeciążeniami zwykłych (nie szablonowych) funkcji?
Odpowiedź: Nie, SFINAE stosuje się tylko do szablonów funkcji lub klas. W przypadku zwykłych funkcji przeciążonych SFINAE nie działa. Często błędnie próbują pisać std::enable_if w parametrach zwykłej funkcji, co nie prowadzi do wyboru, a po prostu czyni sygnaturę nieokreśloną.
Przykład błędnej wersji:
void foo(int, typename std::enable_if<true, int>::type* = nullptr) {} // Błąd: nie szablon
Historia
W bibliotece serializacji podczas implementacji szablonowych funkcji z SFINAE błędnie nie uwzględniono przecięcia warunków dla dwóch specjalizacji. Wynik — sytuacja ambiguous overload i niemożność zbudowania modułu przy pojawieniu się nowego typu danych.
Historia
W starym kodzie biblioteki boost::asio przy błędnej implementacji SFINAE (przez dependent false w static_assert) pojawiały się komunikaty o błędach, które były trudne do odczytania: kompilator rozwijał wszystkie szablony zamiast usunąć niepoprawne opcje. Naprawiono to przez oddzielne enable_if na poziomie sygnatur.
Historia
Przy portowaniu algorytmu wyszukiwania po typach za pomocą SFINAE na stary kompilator (MSVC 2012) zespół napotkał problemy z niepoprawną dekompozycją typów, a wybrany szablon akceptował niewłaściwy typ. Sprawdzanie rozwiązano poprzez type traits przed kompilacją, eliminując podstawienia w samym szablonie.