¿Cómo verificar el tipo de parámetro de plantilla?

97

Supongamos que tengo una función de plantilla y dos clases

class animal {
}
class person {
}

template<class T>
void foo() {
  if (T is animal) {
    kill();
  }
}

¿Cómo hago la verificación para T es animal? No quiero tener algo que verifique durante el tiempo de ejecución. Gracias

WhatABeautifulWorld
fuente
62
Yo pondría "mascota" en lugar de "matar" :-)
JimBamFeng

Respuestas:

135

Utilizar is_same:

#include <type_traits>

template <typename T>
void foo()
{
    if (std::is_same<T, animal>::value) { /* ... */ }  // optimizable...
}

Sin embargo, por lo general, ese es un diseño totalmente inviable y realmente desea especializarse :

template <typename T> void foo() { /* generic implementation  */ }

template <> void foo<animal>()   { /* specific for T = animal */ }

Tenga en cuenta también que es inusual tener plantillas de funciones con argumentos explícitos (no deducidos). No es inaudito, pero a menudo hay mejores enfoques.

Kerrek SB
fuente
2
¡Gracias! En realidad, comparten MUCHO código, por lo que realmente no puedo duplicarlo
WhatABeautifulWorld
3
@WhatABeautifulWorld: siempre puede factorizar su código para que la parte dependiente del tipo pueda relegarse a una función especializable ...
Kerrek SB
1
Un seguimiento rápido, si uso std :: is_same, entonces NO ralentizará el código para otros parámetros de plantilla, ¿verdad?
WhatABeautifulWorld
1
@WhatABeautifulWorld: Los valores de los rasgos se conocen estáticamente. No debería haber ningún costo de tiempo de ejecución, siempre que su compilador sea medio decente. Sin embargo, verifique el ensamblaje si tiene dudas.
Kerrek SB
2
@ AdriC.S .: Como Tno se deduce, no hay mucho que puedas hacer. Puede dejar la plantilla principal sin implementar y crear una especialización, o puede agregar una aserción estática con is_same.
Kerrek SB
38

Creo que hoy en día es mejor usarlo, pero solo con C ++ 17.

#include <type_traits>

template <typename T>
void foo() {
    if constexpr (std::is_same_v<T, animal>) {
        // use type specific operations... 
    } 
}

Si usa algunas operaciones específicas de tipo en el cuerpo de la expresión if sin constexpr, este código no se compilará.

Константин Гудков
fuente
8
en lugar de std::is_same<T, U>::valuepodría usar más corto:std::is_same_v<T, U>
Fureeish
12

En C ++ 17, podemos usar variantes .

Para usar std::variant, debe incluir el encabezado:

#include <variant>

Después de eso, puede agregar std::variantsu código de esta manera:

using Type = std::variant<Animal, Person>;

template <class T>
void foo(Type type) {
    if (std::is_same_v<type, Animal>) {
        // Do stuff...
    } else {
        // Do stuff...
    }
}
Edwin Pratt
fuente
8
¿Cómo se conectan T y Type?
mabraham
4
Esta respuesta es problemática de varias formas. Además de los errores reales ( typeque es el valor de tipo Typeo una plantilla que no tiene sentido aquí) is_same_vno es significativo en el contexto de variant. El "rasgo" correspondiente es holds_alternative.
Pixelchemist
std::variantes totalmente innecesario aquí
tjysdsg
7

Puede especializar sus plantillas en función de lo que se pasa a sus parámetros de esta manera:

template <> void foo<animal> {

}

Tenga en cuenta que esto crea una función completamente nueva basada en el tipo que se pasa como T. Esto suele ser preferible ya que reduce el desorden y es esencialmente la razón por la que tenemos plantillas en primer lugar.

chico de plantilla
fuente
Hmm. ¿Es este método realmente la única forma preferible de especializar el argumento de la plantilla? Digamos que tengo 10 clases secundarias diferentes que necesito administrar dentro de la función de plantilla. ¿Realmente tengo que escribir 10 funciones de plantilla diferentes para la clase respectiva? Creo que me estoy perdiendo el punto central aquí.
Volkan Güven
Sin embargo, esto realmente suena como una buena idea, si alguien no quiere usar type_traits. Como alguien mencionó, la lógica principal se puede hacer en una función diferente, que acepta una bandera adicional para indicar el tipo, y esta declaración especializada puede simplemente establecer la bandera en consecuencia y pasar directamente todos los demás argumentos sin tocar nada. Entonces, si se necesitan manejar 10 clases diferentes, son básicamente 10 líneas para 10 definiciones de funciones diferentes. Pero esto se complicará mucho si hay más de una variable de plantilla.
Harish Ganesan