138

Suppose I have a template function and two classes

class animal {
}
class person {
}

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

How do I do the check for T is animal? I don't want to have something that checks during the run time. Thanks

gsamaras
  • 71,951
  • 46
  • 188
  • 305
WhatABeautifulWorld
  • 3,198
  • 3
  • 22
  • 30

6 Answers6

178

Use is_same:

#include <type_traits>

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

Usually, that's a totally unworkable design, though, and you really want to specialize:

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

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

Note also that it's unusual to have function templates with explicit (non-deduced) arguments. It's not unheard of, but often there are better approaches.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 2
    TThanks! Actually they share A LOT of code so I can not really duplicate it – WhatABeautifulWorld Nov 29 '12 at 23:27
  • 6
    @WhatABeautifulWorld: You can always factor your code so that the type-dependent part can be relegated to a specializable function... – Kerrek SB Nov 29 '12 at 23:27
  • 3
    One quick follow-up, if I do use std::is_same, then it will NOT slow down the code for other template parameters, right? – WhatABeautifulWorld Nov 30 '12 at 17:04
  • 1
    @WhatABeautifulWorld: The trait values are all statically known. There shouldn't be any runtime cost, provided your compiler is half-decent. Check the assembly if in doubt, though. – Kerrek SB Nov 30 '12 at 21:43
  • A question: if I only want to *specialize* the behaviour of the method for *one and only one* specific type, would using `C++11` default template parameters work as well? – Adri C.S. Oct 08 '14 at 14:27
  • 2
    @AdriC.S.: Since `T` isn't deduced, there's not much you can do. You could leave the primary template unimplemented and create a specialization, or you could add a static assertion with `is_same`. – Kerrek SB Oct 08 '14 at 15:20
  • 1
    How to know the type of the argument passed for primitive data types such as if int is passed or float is passed? – Rajesh Apr 16 '18 at 13:16
  • 1
    @Rajesh: how do you mean? The function `foo` has no parameters, so no arguments can be passed to it. You need to specify the template argument directly. And then the code in the answer can test for `int` and `float`, too (`is_same` etc.). – Kerrek SB Apr 16 '18 at 15:17
  • Here is a link to the reference [`std::is_same`](http://www.cplusplus.com/reference/type_traits/is_same/), I was missing it in the answer (but I don't think it's reason enough for an edit). – ph_0 Oct 11 '19 at 14:28
  • I had issues with `std::is_same` and `opencv` dataypes like `cv::Rect`. Is it possible, that `std::is_same` just supports default datatypes? – Markus Dutschke Sep 24 '20 at 11:44
  • @MarkusDutschke What issue did you see using these datatypes with `std::is_same`? Can you describe what happened? – Code Doggo Sep 27 '22 at 03:55
63

I think todays, it is better to use, but this only works with C++17 or later.

#include <type_traits>

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

If you use some type specific operations in if expression body without constexpr, this code will not compile.

Kesto2
  • 70
  • 1
  • 13
  • could you elaborate on why this is better? is it performance or platform related? – serup Nov 26 '21 at 16:18
  • Actually works. Ty! – Youssef Moawad Jan 29 '22 at 04:43
  • 1
    @serup it's better, because it is new, and old things are always worse. `inline constexpr bool is_same_v = is_same::value;` I have no clue why they keep adding these pointless "helpers" which only confuse people instead of fixing their broken language. –  Mar 06 '22 at 09:26
9

std::is_same() is only available since C++11. For pre-C++11 you can use typeid():

template <typename T>
void foo()
{
    if (typeid(T) == typeid(animal)) { /* ... */ }
}
scai
  • 20,297
  • 4
  • 56
  • 72
8

You can specialize your templates based on what's passed into their parameters like this:

template <> void foo<animal> {

}

Note that this creates an entirely new function based on the type that's passed as T. This is usually preferable as it reduces clutter and is essentially the reason we have templates in the first place.

template boy
  • 10,230
  • 8
  • 61
  • 97
  • 1
    Hmm. Is this method really the only preferable way to specialize template argument? Let's say I've 10 different child classes that I need to manage inside the template function. Do I really have to write 10 different template functions for respective class? I think I may be missing the core point here. – knoxgon Aug 07 '17 at 07:18
  • This really sounds like a good idea though, if someone doesn't want to use type_traits. Like someone mentioned the main logic can be done in a different function, which accepts an extra flag to indicate the type, and this specialized declaration can just set the flag accordingly and directly pass on all the other arguments without touching anything. So if 10 different classes need to be handled, it is basically 10 lines for 10 different function definitions. But this will get a lot complicated if there are more than 1 template variable. – Harish Ganesan May 03 '19 at 17:55
7

In C++17, we can use variants.

To use std::variant, you need to include the header:

#include <variant>

After that, you may add std::variant in your code like this:

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...
    }
}
Ed The ''Pro''
  • 875
  • 10
  • 22
  • 15
    How are T and Type connected? – mabraham Sep 18 '18 at 22:14
  • 9
    This answer is problematic in several ways. Besides the actual errors (`type` which is the value of type `Type` or a template which does not make sense here) `is_same_v` is not meaningful in the context of `variant`. The corresponding "trait" is `holds_alternative`. – Pixelchemist May 03 '19 at 20:41
  • `std::variant` is totally unnecessary here – tjysdsg Feb 09 '20 at 09:34
0

use c++ concepts https://en.cppreference.com/w/cpp/language/constraints

for example class who recive only char types

#include <concepts>

 template<typename Type>
    concept CharTypes = std::is_same<Type, char>::value ||
                        std::is_same<Type, wchar_t>::value || std::is_same<Type, char8_t>::value ||
                        std::is_same<Type, char16_t>::value || std::is_same<Type, char32_t>::value;

template<CharTypes T>
    class Some{};

and yes, this not working


    Some<int> s;

andriy-byte
  • 21
  • 1
  • 4