Quiero entrar en más metaprogramación de plantillas. Sé que SFINAE significa "la falla de sustitución no es un error". Pero, ¿alguien puede mostrarme un buen uso de SFINAE?
c++
templates
metaprogramming
sfinae
rlbond
fuente
fuente
Respuestas:
Aquí hay un ejemplo ( de aquí ):
Cuando
IsClassT<int>::Yes
se evalúa, 0 no se puede convertir aint int::*
porque int no es una clase, por lo que no puede tener un puntero de miembro. Si SFINAE no existiera, obtendría un error del compilador, algo como '0 no se puede convertir en puntero de miembro para el tipo int que no es de clase'. En cambio, solo usa el...
formulario que devuelve Two y, por lo tanto, se evalúa como falso, int no es un tipo de clase.fuente
...
, sino elint C::*
, que nunca había visto y tuve que ir a buscar. Encontré la respuesta para lo que es y para qué podría usarse aquí: stackoverflow.com/questions/670734/…Me gusta usar
SFINAE
para verificar condiciones booleanas.Puede resultar muy útil. Por ejemplo, lo usé para verificar si una lista de inicializadores recopilada usando una coma de operador no es más larga que un tamaño fijo
La lista solo se acepta cuando M es menor que N, lo que significa que la lista del inicializador no tiene demasiados elementos.
La sintaxis
char(*)[C]
significa: puntero a una matriz con el tipo de elemento char y tamañoC
. SiC
es falso (0 aquí), entonces obtenemos el tipo no válidochar(*)[0]
, puntero a una matriz de tamaño cero: SFINAE hace que la plantilla sea ignorada en ese momento.Expresado con
boost::enable_if
, se ve asíEn la práctica, a menudo encuentro útil la capacidad de comprobar las condiciones.
fuente
M <= N ? 1 : -1
podría funcionar.int foo[0]
. No me sorprende que sea compatible, ya que permite el truco muy útil de "estructura que termina con una matriz de longitud 0" ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).error C2466: cannot allocate an array of constant size 0
En C ++ 11, las pruebas SFINAE se han vuelto mucho más bonitas. A continuación, se muestran algunos ejemplos de usos comunes:
Elija una sobrecarga de funciones en función de los rasgos
Usando el llamado idioma de sumidero de tipos, puede hacer pruebas bastante arbitrarias en un tipo, como verificar si tiene un miembro y si ese miembro es de cierto tipo
Aquí hay un ejemplo en vivo: http://ideone.com/dHhyHE También escribí recientemente una sección completa sobre SFINAE y envío de etiquetas en mi blog (enchufe descarado pero relevante) http://metaporky.blogspot.de/2014/08/ part-7-static-dispatch-function.html
Tenga en cuenta que a partir de C ++ 14 hay un std :: void_t que es esencialmente el mismo que mi TypeSink aquí.
fuente
TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>
en un lugar y luegoTypeSinkT<decltype(&T::bar)>
en otro? ¿También es&
necesario enstd::declval<T&>
?TypeSink
, C ++ 17 tienestd::void_t
:)La biblioteca enable_if de Boost ofrece una interfaz limpia y agradable para usar SFINAE. Uno de mis ejemplos de uso favoritos está en la biblioteca Boost.Iterator . SFINAE se utiliza para habilitar conversiones de tipo de iterador.
fuente
C ++ 17 probablemente proporcionará un medio genérico para consultar características. Consulte N4502 para obtener más detalles, pero como ejemplo autónomo, considere lo siguiente.
Esta parte es la parte constante, ponla en un encabezado.
El siguiente ejemplo, tomado de N4502 , muestra el uso:
En comparación con las otras implementaciones, esta es bastante simple: un conjunto reducido de herramientas (
void_t
ydetect
) es suficiente. Además, se informó (ver N4502 ) que es considerablemente más eficiente (tiempo de compilación y consumo de memoria del compilador) que los enfoques anteriores.Aquí hay un ejemplo en vivo , que incluye ajustes de portabilidad para GCC pre 5.1.
fuente
Aquí hay otro ejemplo de SFINAE (tardío) , basado en la respuesta de Greg Rogers :
De esta manera, puede verificar el
value
valor de para ver siT
es una clase o no:fuente
int C::*
en su respuesta? ¿Cómo puedeC::*
ser un nombre de parámetro?int C::*
es el tipo de puntero a unaint
variable miembro deC
.Aquí hay un buen artículo de SFINAE: Una introducción al concepto SFINAE de C ++: introspección en tiempo de compilación de un miembro de la clase .
Resúmalo de la siguiente manera:
declval
es una utilidad que le da una "referencia falsa" a un objeto de un tipo que no se puede construir fácilmente.declval
es realmente útil para nuestras construcciones SFINAE.fuente
Aquí, estoy usando la sobrecarga de funciones de plantilla (no directamente SFINAE) para determinar si un puntero es una función o un puntero de clase de miembro: ( ¿Es posible corregir los punteros de función de miembro de iostream cout / cerr que se imprimen como 1 o verdadero? )
https://godbolt.org/z/c2NmzR
Huellas dactilares
Tal como está el código, podría (dependiendo de la "buena" voluntad del compilador) generar una llamada en tiempo de ejecución a una función que devolverá verdadero o falso. Si desea forzar la
is_function_pointer(var)
evaluación en el tipo de compilación (no se realizan llamadas a funciones en tiempo de ejecución), puede usar elconstexpr
truco de la variable:Según el estándar C ++,
constexpr
se garantiza que todas las variables se evaluarán en tiempo de compilación (Calculando la longitud de una cadena C en tiempo de compilación. ¿Es esto realmente un constexpr? ).fuente
El siguiente código usa SFINAE para permitir que el compilador seleccione una sobrecarga en función de si un tipo tiene cierto método o no:
Salida:
fuente