2012-08-16 15 views
19

He estado usando (y visto usado) static_assert para marcar los valores no deseados de los valores de los parámetros de la plantilla. Sin embargo, para todos los casos que encontré parece mejor y más elegante desactivar esos valores no deseados a través de SFINAE. Por ejemploCuándo usar `static_assert` en lugar de SFINAE?

template<typename T, 
     class = std::enable_if<std::is_floating_point<T>::value>::type> 
struct Foo { ... }; 

en lugar de

template<typename T> 
struct Foo { 
    static_assert(std::is_floating_point<T>::value, 
       "Foo<T>: T must be floating point :-("); 
    ... 
}; 

Así que mi pregunta: ¿cuándo se debe utilizar en lugar de static_assert SFINAE y por qué?

EDITAR

creo que lo que he aprendido hasta ahora es la siguiente

SFINAE es una herramienta versátil y poderosa pero potencialmente muy complicado que puede ser usado para muchas tareas, incluyendo resolución de sobrecarga de función (que algunos parecen considerar como su único propósito).

SFINAE se puede utilizar de una manera relativamente simple donde cada vez static_assert puede, excepto que aparece en la declaración (de una clase o función) en lugar de su definición (o es posible insertar a static_assert en, digamos, una declaración de reenvío de clase?). Eso hace que el código sea más literal y, por lo tanto, más claro. Sin embargo, debido a que SFINAE es complicado, tiende a ser más difícil acertar que un simple static_assert.

Por otro lado, static_assert tiene la ventaja de un mensaje de error del compilador más claro, que algunos parecen considerar como el objetivo principal de cualquiera.

+3

¿Puede explicar por qué cree que es mejor con SFINAE? –

+0

Quizás nuestras respuestas estén orientadas por el vocabulario. En su pregunta, creo que debería reemplazar SFINAE por 'std :: enable_if'. Parece extraño querer generar un error con un mecanismo que enfatice el hecho de que no es un error. SFINAE = La falla de sustitución no es un error – log0

Respuesta

6

creo static_assert es el La elección correcta si desea hacer cumplir que T es un tipo de coma flotante. Este método expresa su intención más claramente que la solución SFINAE.

+0

No estoy de acuerdo. considere la declaración directa 'template class some_class;'. ¿Cómo puedo saber que esa clase solo acepta parámetros de plantilla de tipo integral si el 'static_assert' solo aparece en la definición de clase? – Walter

+0

@Walter SFINAE no es aplicable en este caso, entonces, ¿cómo importa? En cualquier caso, con un poco de gimnasia puedes escribir algo como 'template > ...> class some_class;' (donde 'Requires' es un truco como los usados ​​en Boost.Concepts), aunque no puedo garantizar la legitimidad (y la portabilidad) de tal cosa. –

7

static_assert hace que la compilación falle. SFINAE le permite eliminar una posible sobrecarga.

+0

En el caso mostrado, ambos hacen que la compilación falle si escribe 'Foo '. –

+0

SFINAE deshabilita un cierto parámetro de plantilla.Si el compilador no puede encontrar otra coincidencia, la compilación fallará. – Walter

+0

@ R.MartinhoFernandes Acabo de leer su blog/página en * Remastered enable_if * y me faltaba la definición de la plantilla 'all <>' utilizada para combinar varios tipos de condiciones. ¿Dónde puedo encontrarlo? – Walter

12

Utiliza SFINAE, si desea utilizar otra sobrecarga, y static_assert si ninguno de ellos se ajusta a dicho parámetro.

5

Por un lado, usar SFINAE puede dar lugar a que se seleccione otra sobrecarga que originalmente era una peor coincidencia y que no se consideraría.

Y en la situación que hay otras sobrecargas, pero no de ellos es viable, se obtiene algunas cosas buenas como esta:

#include <type_traits> 

void f(int){} 
void f(bool){} 
void f(char){} 
void f(float){} 
void f(long){} 
void f(double){} 
void f(short){} 
void f(unsigned){} 
void f(void*){} 
void f(void (*)()){} 

template<class C, class T = int> 
using EnableIf = typename std::enable_if<C::value, T>::type; 

template<class T> 
struct sfinae_false : std::false_type{}; 

template<class T> 
void f(T&&, EnableIf<sfinae_false<T>> = 0){} 

int main(){ struct X{}; f(X()); } 

Salida:

source.cpp: In function 'int main()': 
source.cpp:23:30: error: no matching function for call to 'f(main()::X)' 
source.cpp:23:30: note: candidates are: 
source.cpp:3:6: note: void f(int) 
source.cpp:3:6: note: no known conversion for argument 1 from 'main()::X' to 'int' 
source.cpp:4:6: note: void f(bool) 
source.cpp:4:6: note: no known conversion for argument 1 from 'main()::X' to 'bool' 
source.cpp:5:6: note: void f(char) 
source.cpp:5:6: note: no known conversion for argument 1 from 'main()::X' to 'char' 
source.cpp:6:6: note: void f(float) 
source.cpp:6:6: note: no known conversion for argument 1 from 'main()::X' to 'float' 
source.cpp:7:6: note: void f(long int) 
source.cpp:7:6: note: no known conversion for argument 1 from 'main()::X' to 'long int' 
source.cpp:8:6: note: void f(double) 
source.cpp:8:6: note: no known conversion for argument 1 from 'main()::X' to 'double' 
source.cpp:9:6: note: void f(short int) 
source.cpp:9:6: note: no known conversion for argument 1 from 'main()::X' to 'short int' 
source.cpp:10:6: note: void f(unsigned int) 
source.cpp:10:6: note: no known conversion for argument 1 from 'main()::X' to 'unsigned int' 
source.cpp:11:6: note: void f(void*) 
source.cpp:11:6: note: no known conversion for argument 1 from 'main()::X' to 'void*' 
source.cpp:12:6: note: void f(void (*)()) 
source.cpp:12:6: note: no known conversion for argument 1 from 'main()::X' to 'void (*)()' 
source.cpp:21:6: note: template<class T> void f(T&&, EnableIf<sfinae_false<T> >) 
source.cpp:21:6: note: template argument deduction/substitution failed: 
+2

Una desagradable propiedad de SFINAE es que debe asegurarse de que haya múltiples sobrecargas tienen condiciones que no se superponen, de lo contrario aún se obtiene un error. Prefiero mucho el envío de etiquetas para la sobrecarga selectiva. – TemplateRex

+1

Dado que el ejemplo en la pregunta es una plantilla de clase en lugar de una plantilla de función, ¿esta respuesta equivale a "usted es correcto para las clases, pero no ha considerado las funciones"? ¿O hay ventajas para las afirmaciones estáticas para las clases también? –

+0

@SteveJessop: Bueno, SFINAE no se aplica realmente a las plantillas de clases, ya que * es * un error difícil allí ("ningún miembro llamado 'tipo' en 'std :: enable_if ' ...). – Xeo

Cuestiones relacionadas