enable_if (Clase)

 

Publicado: octubre de 2016

Para obtener la documentación más reciente de Visual Studio 2017 RC, consulte Documentación de Visual Studio 2017 RC.

Realiza una instancia de manera condicional de un tipo para la resolución de sobrecarga SFINAE. La definición de tipo anidada enable_if<Condition,Type>::type existe y es sinónimo de Type, solo si Condition es true.

template <bool B, class T = void>
struct enable_if;

Parámetros

B
El valor que determina la existencia del tipo resultante.

T
El tipo para crear una instancia si B es true.

Si B es true, enable_if<B, T> tiene una definición de tipo anidada llamada "tipo" que es sinónimo de T.

Si B es false, enable_if<B, T> no tiene una definición de tipo anidada llamada "tipo".

Se proporciona esta plantilla de alias:

template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;

En C++, error en la sustitución de parámetros de plantilla no es un error en sí mismo, esto se conoce como SFINAE (error de sustitución no es un error). Normalmente, enable_if se utiliza para eliminar candidatos de la resolución de sobrecarga, es decir, selecciona los conjuntos de sobrecarga para que una definición se pueda rechazar en favor de otra. Esto se ajusta al comportamiento SFINAE. Para obtener más información sobre SFINAE, vea error de sustitución no es un error en Wikipedia.

A continuación, se presentan cuatro escenarios de ejemplo:

  • Escenario 1: Ajustar el tipo de valor devuelto de una función:
   template <your_stuff>  
typename enable_if<your_condition, your_return_type>::type
   yourfunction(args) {// ...
}
// The alias template makes it more concise:
   template <your_stuff>  
enable_if_t<your_condition, your_return_type>  
yourfunction(args) {// ...
}

  • Escenario 2: Agregar un parámetro de función que tiene un argumento predeterminado:

   template <your_stuff>  
your_return_type_if_present
   yourfunction(args, enable_if_t<your condition, FOO> = BAR) {// ...
}

  • Escenario 3: Agregar un parámetro de plantilla que tiene un argumento predeterminado:

   template <your_stuff, typename Dummy = enable_if_t<your_condition>>  
rest_of_function_declaration_goes_here

  • Escenario 4: Si su función tiene un argumento sin plantilla, puede ajustar su tipo:

   template <typename T>  
void your_function(const T& t,
   enable_if_t<is_something<T>::value, const string&>  
s) {// ...
}

El escenario 1 no funciona con constructores y operadores de conversión porque no tienen tipos de retorno.

El escenario 2 deja el parámetro sin nombre. Podría decir ::type Dummy = BAR, pero el nombre Dummy es irrelevante y si se le da un nombre es probable que desencadene una advertencia "parámetro sin referencia". Debe elegir un tipo de parámetro de función FOO y un argumento predeterminado BAR. Podría decir int y 0, pero entonces los usuarios de su código podrían pasar por error un entero adicional a la función, que se ignoraría. En lugar de eso, recomendamos que utilice void ** y 0 o nullptr, porque casi nada se puede convertir a void **:

template <your_stuff>  
your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type = nullptr) {// ...
}

El escenario 2 también funciona para constructores ordinarios. Sin embargo, no funciona para operadores de conversión, porque no pueden tomar parámetros adicionales. Tampoco funciona para variádicas constructores porque añadir parámetros adicionales convierte el parámetro de función paquete un contexto no deducido y, por tanto, se anula la finalidad de enable_if.

El escenario 3 utiliza el nombre Dummy, pero es opcional. Simplemente " typename = typename" podría funcionar, pero si cree que parece raro, puede usar un nombre "ficticio", simplemente no utilizar uno que también podrían utilizarse en la definición de función. Si no da un tipo a enable_if, se anula de manera predeterminada y esto es perfectamente razonable porque a usted le da igual qué es Dummy. Esto funciona para todo, incluidos los operadores de conversión y variádicas constructores.

El escenario 4 funciona en constructores que no tienen tipos de valor devueltos y, de este modo, soluciona la limitación del ajuste del escenario 1. Sin embargo, el escenario 4 se limita a argumentos de función sin plantilla, que no siempre están disponibles. (Utilizar el escenario 4 en un argumento de función con plantilla evita que la deducción de argumentos de plantilla funcione en él.)

enable_if es eficaz, pero también peligroso si se utiliza incorrectamente. Como su objetivo es hacer desaparecer a los candidatos antes de la resolución de sobrecarga, si se utiliza incorrectamente, sus efectos pueden ser muy confusos. A continuación se muestran algunas recomendaciones:

  • No utilice enable_if para seleccionar entre implementaciones en tiempo de compilación. No escriba nunca un enable_if para CONDITION y otro para !CONDITION. En su lugar, use un etiqueta envío patrón: por ejemplo, un algoritmo que seleccione implementaciones en función de las ventajas de los iteradores le asigna.

  • No utilice enable_if para exigir requisitos. Si desea validar los parámetros de plantilla y se produce un error en la validación, provoque un error en lugar de seleccionar otra implementación, utilice static_assert.

  • Utilice enable_if cuando tenga un conjunto de sobrecarga que convierta en ambiguos ciertos códigos que en otras circunstancias serían buenos. Frecuentemente, esto sucede en constructores que convierten implícitamente.

En este ejemplo se explica cómo la función de plantilla STL std::make_pair() aprovecha enable_if.

void func(const pair<int, int>&);

void func(const pair<string, string>&);

func(make_pair("foo", "bar"));

En este ejemplo, make_pair("foo", "bar") devuelve pair<const char *, const char *>. La resolución de sobrecarga debe determinar qué func() quiere. pair<A, B> tiene un constructor que convierte implícitamente de pair<X, Y>. Esto no es nuevo, ya sucedía en C++98. No obstante, en C++98/03, la firma del constructor que convierte implícitamente existe siempre, incluso si es pair<int, int>(const pair<const char *, const char *>&). A la resolución de sobrecarga no le importa que un intento de crear una instancia de este constructor explote horriblemente, porque const char * no es implícitamente convertible a int, solo examina firmas antes de que se creen instancias de las definiciones de la función. Por lo tanto, el código de ejemplo es ambiguo, porque las firmas existen para convertir pair<const char *, const char *> tanto a pair<int, int> y a pair<string, string>.

C ++&11; solucionó esta ambigüedad utilizando enable_if para asegurarse de que pair<A, B>(const pair<X, Y>&) existe sólo cuando const X& es implícitamente convertible a A y const Y& es implícitamente convertible a B. Esto permite que la resolución de sobrecarga determine que pair<const char *, const char *> no se puede convertir en pair<int, int> y que la sobrecarga que toma pair<string, string> es viable.

Encabezado:<type_traits></type_traits>

Espacio de nombres: std

<type_traits></type_traits>

Mostrar: