Espacios de nombres
Variantes
Acciones

std::enable_if

De cppreference.com
< cpp‎ | types
 
 
Biblioteca de servicios
 
Apoyo de tipos
Tipos básicos
Tipos fundamentales
Tipos enteros de anchura fija (C++11)
Límites numéricos
Interfaz de C de límites numéricos
Información de tipo
en tiempo de ejecución
Rasgos de tipos
Categorías de tipos
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Propiedades de tipos
(C++11)
(C++11)
(C++14)
(C++11)
(C++11)(hasta C++20)
(C++11)(en desuso en C++20)
(C++11)
Constantes de rasgos de tipos
Metafunciones
(C++17)
Contexto de evaluación constante
Operaciones soportadas
Relaciones y consultas de propiedades
Modificaciones de tipos
(C++11)(C++11)(C++11)
Transformaciones de tipos
(C++11)
enable_if
(C++11)
(C++17)
(C++11)(hasta C++20)(C++17)
 
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) [editar]