std:: enable_if
|
Definido en el encabezado
<type_traits>
|
||
|
template
<
bool
B,
class
T
=
void
>
struct enable_if ; |
(desde C++11) | |
Si
B
es
true
,
std::enable_if
tiene un typedef miembro público
type
, igual a
T
; de lo contrario, no existe el typedef miembro.
Esta metafunción es una forma conveniente de aprovechar SFINAE antes de los concepts de C++20, en particular para eliminar condicionalmente funciones del candidate set basándose en type traits, permitiendo sobrecargas de funciones separadas o especializaciones basadas en esos diferentes type traits.
std::enable_if
puede utilizarse de muchas formas, incluyendo:
- como un argumento de función adicional (no aplicable a la mayoría de las sobrecargas de operadores),
- como un tipo de retorno (no aplicable a constructores y destructores),
- como un parámetro de plantilla de clase o plantilla de función.
Si el programa añade especializaciones para
std::enable_if
, el comportamiento no está definido.
Contenidos |
Tipos de miembros
| Tipo | Definición |
type
|
ya sea
T
o ningún miembro de este tipo, dependiendo del valor de
B
|
Tipos auxiliares
|
template
<
bool
B,
class
T
=
void
>
using enable_if_t = typename enable_if < B,T > :: type ; |
(desde C++14) | |
Implementación posible
template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; |
Notas
Un error común es declarar dos plantillas de función que solo difieren en sus argumentos de plantilla predeterminados. Esto no funciona porque las declaraciones se tratan como redeclaraciones de la misma plantilla de función (los argumentos de plantilla predeterminados no se tienen en cuenta en equivalencia de plantillas de función ).
/* INCORRECTO */ struct T { enum { int_t, float_t } type; template<typename Integer, typename = std::enable_if_t<std::is_integral<Integer>::value>> T(Integer) : type(int_t) {} template<typename Floating, typename = std::enable_if_t<std::is_floating_point<Floating>::value>> T(Floating) : type(float_t) {} // error: tratado como redefinición }; /* CORRECTO */ struct T { enum { int_t, float_t } type; template<typename Integer, std::enable_if_t<std::is_integral<Integer>::value, bool> = true> T(Integer) : type(int_t) {} template<typename Floating, std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true> T(Floating) : type(float_t) {} // OK };
Se debe tener cuidado al usar
enable_if
en el tipo de un parámetro de plantilla constante de una plantilla de función en el ámbito del espacio de nombres. Algunas especificaciones ABI como la ABI de Itanium no incluyen las partes dependientes de la instanciación de los parámetros de plantilla constantes en el proceso de name mangling, lo que significa que especializaciones de dos plantillas de función distintas podrían terminar con el mismo nombre alterado y ser enlazadas erróneamente. Por ejemplo:
// primera unidad de traducción struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value1, int> = 0> void func() {} // #1 template void func<X>(); // #2 // segunda unidad de traducción struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value2, int> = 0> void func() {} // #3 template void func<X>(); // #4
Las plantillas de función #1 y #3 tienen firmas diferentes y son plantillas distintas. Sin embargo, #2 y #4, a pesar de ser instanciaciones de diferentes plantillas de función, tienen el mismo nombre ofuscado
en el Itanium C++ ABI
(
_Z4funcI1XLi0EEvv
), lo que significa que el enlazador considerará erróneamente que son la misma entidad.
Ejemplo
#include <iostream> #include <new> #include <string> #include <type_traits> namespace detail { void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); } } // #1, habilitado mediante el tipo de retorno template<class T> typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type construct(T*) { std::cout << "construcción por defecto de T trivialmente construible por defecto\n"; } // igual que arriba template<class T> typename std::enable_if<!std::is_trivially_default_constructible<T>::valor>::type construct(T* p) { std::cout << "construcción por defecto de T no trivialmente construible por defecto\n"; ::new(detail::voidify(p)) T; } // #2 template<class T, class... Args> std::enable_if_t<std::is_constructible<T, Args&&...>::value> // Usando tipo auxiliar construct(T* p, Args&&... args) { std::cout << "construyendo T con operación\n"; ::new(detail::voidify(p)) T(static_cast<Args&&>(args)...); } // #3, habilitado mediante un parámetro template<class T> void destroy( T*, typename std::enable_if< std::is_trivially_destructible<T>::valor >::type* = 0) { std::cout << "destruyendo T trivialmente destructible\n"; } // #4, habilitado mediante un parámetro de plantilla constante template<class T, typename std::enable_if< !std::is_trivially_destructible<T>{} && (std::is_class<T>{} || std::is_union<T>{}), bool>::type = true> void destroy(T* t) { std::cout << "destruyendo T no trivialmente destructible\n"; t->~T(); } // #5, habilitado mediante un parámetro de plantilla de tipo template<class T, typename = std::enable_if_t<std::is_array<T>::valor>> void destroy(T* t) // nota: la firma de la función no está modificada { for (std::size_t i = 0; i < std::extent<T>::valor; ++i) destroy((*t)[i]); } /* template<class T, typename = std::enable_if_t<std::is_void<T>::value>> void destroy(T* t) {} // error: tiene la misma firma que #5 */ // la especialización parcial de A está habilitada mediante un parámetro de plantilla template<class T, class Enable = void> class A {}; // plantilla principal template<class T> class A<T, typename std::enable_if<std::is_floating_point<T>::valor>::type> {}; // especialización para tipos de punto flotante int main() { union { int i; char s[sizeof(std::string)]; } u; construct(reinterpret_cast<int*>(&u)); destroy(reinterpret_cast<int*>(&u)); construct(reinterpret_cast<std::string*>(&u), "Hola"); destroy(reinterpret_cast<std::string*>(&u)); A<int>{}; // OK: coincide con la plantilla principal A<double>{}; // OK: coincide con la especialización parcial }
Salida:
construcción por defecto de T trivialmente construible por defecto destrucción de T trivialmente destructible construcción de T con operación destrucción de T no trivialmente destructible
Véase también
|
(C++17)
|
plantilla de alias variádica void
(plantilla de alias) |