Como verificar o tipo de parâmetro de modelo?

95

Suponha que eu tenha uma função de modelo e duas classes

class animal {
}
class person {
}

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

Como faço para verificar se é animal? Não quero ter algo que verifique durante o tempo de execução. obrigado

WhatABeautifulWorld
fonte
58
Eu colocaria "pet" em vez de "kill" :-)
JimBamFeng

Respostas:

132

Use is_same:

#include <type_traits>

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

Normalmente, esse é um design totalmente impraticável, e você realmente quer se especializar :

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

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

Observe também que é incomum ter modelos de função com argumentos explícitos (não deduzidos). Não é inédito, mas geralmente existem abordagens melhores.

Kerrek SB
fonte
2
Obrigada! Na verdade, eles compartilham MUITO código, então não posso duplicá-lo
WhatABeautifulWorld
3
@WhatABeautifulWorld: Você sempre pode fatorar seu código para que a parte dependente do tipo possa ser relegada a uma função especializável ...
Kerrek SB
1
Um rápido acompanhamento, se eu usar std :: is_same, então NÃO irá desacelerar o código para outros parâmetros de template, certo?
WhatABeautifulWorld
1
@WhatABeautifulWorld: Os valores dos traços são todos estaticamente conhecidos. Não deve haver nenhum custo de tempo de execução, desde que seu compilador seja decente. Verifique a montagem em caso de dúvida.
Kerrek SB
2
@ AdriC.S .: Como Tnão se deduz, não há muito que você possa fazer. Você pode deixar o modelo primário sem implementação e criar uma especialização ou pode adicionar uma declaração estática com is_same.
Kerrek SB
35

Acho que hoje é melhor usar, mas só com C ++ 17.

#include <type_traits>

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

Se você usar algumas operações específicas de tipo em if expression body sem constexpr, este código não será compilado.

Константин Гудков
fonte
8
em vez de std::is_same<T, U>::valuevocê poderia usar mais curto:std::is_same_v<T, U>
Fureeish
9

Em C ++ 17, podemos usar variantes .

Para usar std::variant, você precisa incluir o cabeçalho:

#include <variant>

Depois disso, você pode adicionar std::variantseu código da seguinte forma:

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
fonte
8
Como T e Type são conectados?
mabraham
4
Essa resposta é problemática de várias maneiras. Além dos erros reais ( typeque é o valor do tipo Typeou um modelo que não faz sentido aqui) is_same_vnão é significativo no contexto de variant. O "traço" correspondente é holds_alternative.
Pixelchemist
std::varianté totalmente desnecessário aqui
tjysdsg
7

Você pode especializar seus modelos com base no que é passado em seus parâmetros, como este:

template <> void foo<animal> {

}

Observe que isso cria uma função totalmente nova com base no tipo que é passado como T. Isso geralmente é preferível, pois reduz a desordem e é essencialmente a razão pela qual temos modelos em primeiro lugar.

menino modelo
fonte
Hmm. Este método é realmente a única maneira preferível de especializar o argumento do modelo? Digamos que eu tenha 10 classes filhas diferentes que preciso gerenciar dentro da função de modelo. Eu realmente preciso escrever 10 funções de modelo diferentes para a classe respectiva? Acho que posso estar perdendo o ponto central aqui.
Volkan Güven
Porém, isso realmente parece uma boa ideia, se alguém não quiser usar type_traits. Como alguém mencionou, a lógica principal pode ser feita em uma função diferente, que aceita um sinalizador extra para indicar o tipo, e essa declaração especializada pode apenas definir o sinalizador de acordo e passar diretamente todos os outros argumentos sem tocar em nada. Portanto, se 10 classes diferentes precisam ser manipuladas, são basicamente 10 linhas para 10 definições de função diferentes. Mas isso será muito complicado se houver mais de 1 variável de modelo.
Harish Ganesan