SFINAE (Substitution Failure Is Not An Error) — es un mecanismo clave de especialización de plantillas en C++. Si al seleccionar una plantilla la definición se vuelve incorrecta (por ejemplo, cuando la sustitución de tipo provoca un error en la etapa de selección de la plantilla), esto no es un error de compilación, sino que simplemente hace que la plantilla no esté disponible para la selección.
SFINAE es la base de herramientas como std::enable_if, varios detectores de tipo (type traits), implementa el patrón de despacho por etiqueta (tag dispatching).
Ejemplo correcto:
#include <type_traits> // Para enteros template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type foo(T) { std::cout << "Integral "; } // Para otros template<typename T> typename std::enable_if<!std::is_integral<T>::value, void>::type foo(T) { std::cout << "Not integral "; }
Uso incorrecto: Se puede obtener accidentalmente ambigüedad en la llamada (ambiguous overload) o provocar un error de compilación si no se describen cuidadosamente las condiciones para std::enable_if o especializaciones.
¿Se puede usar SFINAE para elegir entre sobrecargas de funciones normales (no plantillas)?
Respuesta: No, SFINAE se aplica solo a funciones o clases plantillas. Para funciones sobrecargadas normales, SFINAE no funciona. A menudo se intenta erróneamente escribir std::enable_if en los parámetros de una función normal, lo que no lleva a una selección, sino que simplemente hace que la firma sea indefinida.
Ejemplo de un caso erróneo:
void foo(int, typename std::enable_if<true, int>::type* = nullptr) {} // Error: no es una plantilla
Historia
En la biblioteca de serialización, al implementar funciones plantilla con SFINAE, se pasó por alto la intersección de condiciones para dos especializaciones. El resultado fue una situación de ambigüedad en la sobrecarga y la incapacidad de compilar el módulo al aparecer un nuevo tipo de datos.
Historia
En el código antiguo de la biblioteca boost::asio, en una implementación errónea de SFINAE (a través de dependent false en static_assert), aparecían mensajes de error que no se podían leer: el compilador expandía todas las plantillas en lugar de eliminar las opciones incorrectas. Se corrigió mediante enable_if separados a nivel de firmas.
Historia
Al portar la búsqueda de algoritmos por tipos a través de SFINAE a un compilador antiguo (MSVC 2012), el equipo se encontró con que la descomposición de tipos no se realizaba correctamente, y la plantilla seleccionada aceptaba un tipo incorrecto. La verificación se resolvió a través de type traits antes de la compilación, eliminando la sustitución en la propia plantilla.