Estoy usando la función SFINAE en gran medida en un proyecto y no estoy seguro de si hay alguna diferencia entre los siguientes dos enfoques (aparte del estilo):
#include <cstdlib>
#include <type_traits>
#include <iostream>
template <class T, class = std::enable_if_t<std::is_same_v<T, int>>>
void foo()
{
std::cout << "method 1" << std::endl;
}
template <class T, std::enable_if_t<std::is_same_v<T, double>>* = 0>
void foo()
{
std::cout << "method 2" << std::endl;
}
int main()
{
foo<int>();
foo<double>();
std::cout << "Done...";
std::getchar();
return EXIT_SUCCESS;
}
El resultado del programa es el esperado:
method 1
method 2
Done...
He visto que el método 2 se usa con más frecuencia en stackoverflow, pero prefiero el método 1.
¿Hay alguna circunstancia cuando estos dos enfoques difieren?
Respuestas:
Sugerencia: prefiera el método 2.
Ambos métodos funcionan con funciones individuales. El problema surge cuando tiene más de una función, con la misma firma, y desea habilitar solo una función del conjunto.
Supongamos que desea habilitar
foo()
, versión 1, cuandobar<T>()
(pretenda que es unaconstexpr
función) estrue
, yfoo()
, versión 2, cuándobar<T>()
esfalse
.Con
aparece un error de compilación porque tiene una ambigüedad: dos
foo()
funciones con la misma firma (un parámetro de plantilla predeterminado no cambia la firma).Pero la siguiente solución
funciona, porque SFINAE modifica la firma de las funciones.
Observación no relacionada: también hay un tercer método: habilitar / deshabilitar el tipo de retorno (excepto para los constructores de clase / estructura, obviamente)
Como método 2, el método 3 es compatible con la selección de funciones alternativas con la misma firma.
fuente
foo()
función permanece disponible cuando lo llama con un segundo parámetro de plantilla explícito (lafoo<double, double>();
llamada). Y si permanece disponible, existe una ambigüedad con la otra versión. Con el método 2, SFINAE habilita / deshabilita el segundo argumento, no el parámetro predeterminado. Por lo tanto, no puede llamarlo explicando el parámetro porque hay una falla de sustitución que no permite un segundo parámetro. Así que la versión no está disponible, por lo que no hay ambigüedadauto foo() -> std::enable_if_t<...>
menudo es útil para evitar ocultar la firma de la función y permitir el uso de los argumentos de la función.Además de la respuesta de max66 , otra razón para preferir el método 2 es que con el método 1, puede (accidentalmente) pasar un parámetro de tipo explícito como el segundo argumento de plantilla y derrotar completamente el mecanismo SFINAE. Esto podría suceder como un error tipográfico, copiar / pegar, o como un descuido en un mecanismo de plantilla más grande.
Demostración en vivo aquí
fuente