c++ - ¿Por qué enable_if_t en los argumentos de la plantilla se queja de redefiniciones?
templates c++14 (4)
Tengo el siguiente caso que funciona usando std::enable_if
:
template<typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr>
void f() { }
Ahora, vi en cppreference la nueva sintaxis, mucho más limpia en mi opinión: typename = std::enable_if_t<std::is_same<int, T>::value>>
Quería portar mi código:
template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>>
void g() { }
Pero ahora GCC (5.2) se queja:
error: redefinition of ''template<class T, class> void g()''
void g() { }
Por qué es así ? ¿Qué puedo hacer para tener la sintaxis nueva y más concisa en este caso si esto es posible?
Para un tipo de retorno de función, está buscando lo siguiente:
template<typename T> std::enable_if_t< conditional, instantiation result > foo();
Ejemplo:
#include <iostream>
// when T is "int", replace with ''void foo()''
template<typename T>
std::enable_if_t<std::is_same<int, T>::value, void> foo() {
std::cout << "foo int/n";
}
template<typename T>
std::enable_if_t<std::is_same<float, T>::value, void> foo() {
std::cout << "foo float/n";
}
int main() {
foo<int>();
foo<float>();
}
ver también
Te falta un ":: tipo" ..
template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>::type>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>::type>
void g() { }
Vamos a eliminar algunos códigos.
template<
class T,
class U/* = std::enable_if_t<std::is_same<int, T>::value>*/
>
void g() { }
template<
class T,
class U/* = std::enable_if_t<std::is_same<double, T>::value>*/
>
void g() { }
¿Te sorprendería si el compilador rechazara las dos plantillas anteriores?
Ambas son funciones de plantilla de "tipo" template<class,class>void()
. El hecho de que el segundo argumento de tipo tenga un valor predeterminado diferente no importa. Sería como esperar que se sobrecarguen dos funciones diferentes de print(string, int)
con diferentes valores int
predeterminados. ;)
En el primer caso tenemos:
template<
typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr
>
void f() { }
template<
typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr
>
void f() { }
Aquí no podemos eliminar la cláusula enable_if
. Actualizando a enable_if_t
:
template<
class T,
std::enable_if_t<std::is_same<int, T>::value, int>* = nullptr
>
void f() { }
template<
class T,
std::enable_if_t<std::is_same<double, T>::value, int>* = nullptr
>
void f() { }
También sustituí un uso de typename
con class
. Sospecho que su confusión se debió a que typename
tiene dos significados: uno como marcador para un tipo de argumento de template
y otro como un desambiguador para un tipo dependiente.
Aquí el segundo argumento es un puntero, cuyo tipo depende del primero. El compilador no puede determinar si estos dos conflictos sin antes sustituirlos en el tipo T
, y usted notará que en realidad nunca entrarán en conflicto.
enable_if_t<B>
es solo un alias para typename enable_if<B>::type
. Sustituyamos eso en g
para que podamos ver la diferencia real entre f
y g
:
template<typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename = typename std::enable_if<std::is_same<int, T>::value>::type>
void g() { }
template<typename T,
typename = typename std::enable_if<std::is_same<double, T>::value>::type>
void g() { }
En el caso de f
, tenemos dos plantillas de función con parámetros de plantilla <typename, X*>
, donde el tipo X
depende del tipo del primer argumento de la plantilla. En el caso de g
tenemos dos plantillas de función con parámetros de plantilla <typename, typename>
y es el argumento de la plantilla por defecto el que es dependiente, por lo que C ++ considera que ambas declaran la misma entidad.
Cualquiera de los dos estilos se puede usar con el alias enable_if_t
:
template<typename T,
std::enable_if_t<std::is_same<int, T>::value>* = nullptr>
void f() { }
template<typename T,
std::enable_if_t<std::is_same<double, T>::value>* = nullptr>
void f() { }
template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>>
void g() { }