Как проверить тип параметра шаблона?

97

Предположим, у меня есть шаблонная функция и два класса

class animal {
}
class person {
}

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

Как мне проверить, является ли T животное? Я не хочу, чтобы что-то проверяло во время выполнения. Благодарность

WhatABeautifulWorld
источник
62
Я бы поставил «питомец» вместо «убить» :-)
JimBamFeng

Ответы:

135

Использование is_same:

#include <type_traits>

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

Обычно это совершенно неработающий дизайн, и вы действительно хотите специализироваться :

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

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

Также обратите внимание, что шаблоны функций с явными (невыведенными) аргументами необычны. Это не редкость, но часто есть подходы получше.

Керрек С.Б.
источник
2
Спасибо! На самом деле у них МНОГО кода, поэтому я не могу его дублировать
WhatABeautifulWorld
3
@WhatABeautifulWorld: Вы всегда можете разложить свой код на множители, чтобы часть, зависящую от типа, можно было отнести к специализированной функции ...
Керрек С.Б.
1
Одно быстрое наблюдение: если я использую std :: is_same, то это НЕ замедлит код для других параметров шаблона, верно?
WhatABeautifulWorld
1
@WhatABeautifulWorld: все значения признаков известны статически. Не должно быть никаких затрат времени выполнения, если ваш компилятор наполовину приличный. Однако проверьте сборку, если сомневаетесь.
Kerrek SB
2
@ AdriC.S .: Поскольку Tне выводится, вы мало что можете сделать. Вы можете оставить основной шаблон нереализованным и создать специализацию или добавить статическое утверждение с помощью is_same.
Kerrek SB 08
38

Думаю, сегодня лучше использовать, но только с C ++ 17.

#include <type_traits>

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

Если вы используете какие-либо операции, специфичные для типа, в теле выражения if без constexpr, этот код не будет компилироваться.

Константин Гудков
источник
8
вместо этого std::is_same<T, U>::valueможно было бы использовать короче:std::is_same_v<T, U>
Fureeish
12

В C ++ 17 мы можем использовать варианты .

Для использования std::variantвам необходимо включить заголовок:

#include <variant>

После этого вы можете добавить std::variantв свой код вот так:

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...
    }
}
Эдвин Пратт
источник
8
Как связаны T и Type?
mabraham
4
Этот ответ проблематичен по нескольким причинам. Помимо фактических ошибок ( typeзначение типа Typeили шаблона, которые здесь не имеют смысла) is_same_vне имеют смысла в контексте variant. Соответствующая «черта» есть holds_alternative.
Pixelchemist
std::variantздесь совершенно не нужно
tjysdsg
7

Вы можете специализировать свои шаблоны на основе того, что передается в их параметры, например:

template <> void foo<animal> {

}

Обратите внимание, что при этом создается совершенно новая функция на основе переданного типа T. Обычно это предпочтительнее, так как это уменьшает беспорядок и, по сути, является причиной того, что у нас вообще есть шаблоны.

шаблон мальчик
источник
Хм. Действительно ли этот метод является единственным предпочтительным способом специализации аргумента шаблона? Допустим, у меня есть 10 различных дочерних классов, которыми мне нужно управлять внутри функции шаблона. Мне действительно нужно писать 10 различных шаблонных функций для соответствующего класса? Думаю, здесь я упускаю главное.
Volkan Güven
Это действительно звучит как хорошая идея, если кто-то не хочет использовать type_traits. Как уже упоминалось, основная логика может быть реализована в другой функции, которая принимает дополнительный флаг для указания типа, и это специализированное объявление может просто установить флаг соответствующим образом и напрямую передавать все другие аргументы, не касаясь ничего. Таким образом, если необходимо обработать 10 различных классов, это в основном 10 строк для 10 различных определений функций. Но это будет намного сложнее, если существует более одной переменной шаблона.
Хариш Ганесан,