SFINAE
Habilite funciones de forma condicional
SFINAE es una lección gratuita de C++ Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de C++ Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de C++ Academy incluye 4 lecciones en total.
¿Qué significa SFINAE?
SFINAE significa «un fallo de sustitución no es un error». Cuando el compilador sustituye los argumentos de plantilla y el resultado no es válido, ese candidato simplemente se elimina en lugar de producir un error irrecuperable.
- Permite crear sobrecargas condicionales.
- Es la base de la selección en tiempo de compilación.
#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;
}El fallo de sustitución en acción
Si sustituir un tipo hace que la firma de una función no sea válida, el compilador la omite silenciosamente y sigue buscando entre los demás candidatos.
#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;
}Dos sobrecargas, una gana
Proporcione dos sobrecargas de modo que exactamente una se sustituya correctamente para un tipo determinado. SFINAE descarta la que no sea válida.
#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 y SFINAE basado en expresiones
El uso de decltype sobre una expresión en el tipo de retorno permite que una función exista solo si esa expresión es válida para el 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;
}Detección de un miembro
Un recurso clásico de SFINAE detecta si un tipo tiene un miembro concreto comprobando si compila una expresión que lo utiliza.
#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;
}La alternativa ...
Una sobrecarga que acepta ... (argumentos variables) tiene la prioridad más baja. Sirve como alternativa general cuando falla la sobrecarga restringida mediante SFINAE.
#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
El std::void_t de C++17 simplifica la detección: transforma cualquier tipo válido en void, de modo que una especialización solo se activa cuando la expresión comprobada es válida.
#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;
}¿Por qué no producir simplemente un error?
Sin SFINAE, una sustitución no válida produciría un error de compilación irrecuperable del que no se podría recuperar la ejecución. SFINAE la convierte silenciosamente en «no es un 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
SFINAE puede resultar difícil de leer. El tag dispatch selecciona una implementación mediante pequeños tipos etiqueta y la resolución de sobrecargas.
#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 if constexpr
En C++17, if constexpr suele sustituir a SFINAE para ramificar dentro de una sola función, ya que las ramas no utilizadas se descartan en tiempo de compilación.
#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;
}Integración
SFINAE resulta especialmente útil cuando debe seleccionar entre distintas plantillas de función en función de las propiedades de un tipo, en el nivel de sobrecarga.
#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;
}Comprobación rápida
Compruebe su comprensión de SFINAE.
Resumen
Ha aprendido sobre SFINAE:
- una sustitución no válida elimina un candidato en lugar de producir un error
- se utiliza con
enable_if,decltypeyvoid_tpara habilitar funciones de forma condicional - una sobrecarga
...actúa como alternativa - las alternativas modernas incluyen tag dispatch e
if constexpr
A continuación, se centrará en detalle en los patrones clásicos de enable_if.
Preguntas frecuentes
¿La lección «SFINAE» es gratis?
Sí — el texto completo de «SFINAE» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de C++ Academy, actualiza a CoddyKit PRO. El curso de C++ Academy incluye 4 lecciones en total.
¿Qué aprenderé en «SFINAE»?
Habilite funciones de forma condicional Practicas C++ Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar C++ Academy?
No se requiere experiencia previa. C++ Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «SFINAE»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de C++ Academy?
Sí. Cada lección de C++ Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.