std::enable_if
Definido en el archivo de encabezado <type_traits>
|
||
template< bool B, class T = void > struct enable_if; |
(desde C++11) | |
Si B
es true, std::enable_if tiene un miembro typedef público type
igual a T
; de lo contrario, no hay un miembro typedef.
Esta metafunción es una forma conveniente de aprovechar SFINAE para eliminar condicionalmente funciones de resolución de sobrecarga según los rasgos de tipo y para proporcionar sobrecargas de funciones y especializaciones independientes para diferentes rasgos de tipo. std::enable_if se puede usar como un argumento de función adicional (no aplicable a sobrecargas de operadores), como un tipo de retorno (no aplicable a constructores y destructores), o como plantilla de clase o parámetro de plantilla de función.
El comportamiento de un programa que añade especializaciones para enable_if
no está definido.
Contenido |
[editar] Tipos miembro
Tipo | Definición |
type
|
O bien T o no tal miembro, dependiendo del valor de B .
|
[editar] Tipos auxiliares
template< bool B, class T = void > using enable_if_t = typename enable_if<B,T>::type; |
(desde C++14) | |
[editar] Posible implementación
template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; |
[editar] Notas
Un error común es declarar dos plantillas de función que solo difieren en sus argumentos de plantilla por defecto. Esto no funciona porque las declaraciones se tratan como redeclaraciones de la misma plantilla de función (los argumentos de plantilla por defecto no se tienen en cuenta en equivalencia de plantilla de función).
/* EQUIVOCADO */ struct T { enum { int_t,float_t } m_type; template <typename Integer, typename = std::enable_if_t<std::is_integral<Integer>::value> > T(Integer) : m_type(int_t) {} template <typename Floating, typename = std::enable_if_t<std::is_floating_point<Floating>::value> > T(Floating) : m_type(float_t) {} // ERROR: se trata como una redefinición }; /* CORRECTO */ struct T { enum { int_t,float_t } m_type; template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value, int> = 0 > T(Integer) : m_type(int_t) {} template <typename Floating, std::enable_if_t<std::is_floating_point<Floating>::value, int> = 0 > T(Floating) : m_type(float_t) {} // de acuerdo };
Se debe tener cuidado al usar enable_if
en el tipo de un parámetro de no tipo de plantilla de una plantilla de función en el ámbito del espacio de nombres. Algunas especificaciones ABI como la Itanium ABI no incluyen las partes de instanciación dependiente de los parámetros de plantilla que no son de tipo en la alteración (mangling), lo que significa que las especializaciones de dos plantillas de función distintas pueden terminar con el mismo nombre alterado y ser enlazadas erróneamente. Por ejemplo:
// priimera 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 distintas signaturas y son plantillas distintas. Sin embargo, #2 y #4, a pesar de ser instanciaciones de distintas plantillas de función, tienen el mismo nombre alterado (mangled) en la Itanium ABI de C++ (_Z4funcI1XLi0EEvv
), lo que significa que el enlazador las considerará erróneamente como la misma entidad.
[editar] Ejemplo
#include <type_traits> #include <new> #include <iostream> #include <string> namespace detail { void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); } } // #1, habilitada via el tipo de retorno template<class T> typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type construct(T*) { std::cout << "\n"; } // lo mismo que arriba template<class T> typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type construct(T* p) { std::cout << "construyendo por defecto una 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 operacion\n"; ::new(detail::voidify(p)) T(static_cast<Args&&>(args)...); } // #3, habilitada via un parámetro template<class T> void destroy( T*, typename std::enable_if< std::is_trivially_destructible<T>::value >::type* = 0 ){ std::cout << "destruyendo una T trivialmente destruible\n"; } // #4, habilitada via un parámetro de plantilla sin tipo template<class T, typename std::enable_if< !std::is_trivially_destructible<T>{} && (std::is_class<T>{} || std::is_union<T>{}), int>::type = 0> void destroy(T* t) { std::cout << "destruyendo una T no trivialmente destruible\n"; t->~T(); } // #5, habilitada via un parámetro de plantilla con tipo template<class T, typename = std::enable_if_t<std::is_array<T>::value> > void destroy(T* t) // nota: la signatura de la función no se modifica { for(std::size_t i = 0; i < std::extent<T>::value; ++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 signatura que #5 */ // la especialización parcial de A se habilita via un parámetro de plantilla template<class T, class Enable = void> class A {}; // plantilla primaria template<class T> class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> { }; // especialización para tipos de punto flotante int main() { std::aligned_union_t<0,int,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>{}; // de acuerdo: coincide con la plantilla primaria A<double>{}; // de acuerdo: coincide con la especialización parcial }
Salida:
construyendo por defecto una T trivialmente construible por defecto destruyendo una T trivialmente destruible construyendo T con operacion destruyendo una T no trivialmente destruible
[editar] Véase también
(C++17) |
Plantilla de alias void variádica. (plantilla de alias) |