31

Is it possible in C++ to check the type passed into a template function? For example:

template <typename T>
void Foo()
{
   if (typeof(SomeClass) == T)
      ...;
   else if (typeof(SomeClass2) == T)
      ...;
}
SCFrench
  • 8,244
  • 2
  • 31
  • 61
Max Frai
  • 61,946
  • 78
  • 197
  • 306

4 Answers4

44

Yes, it is...but it probably won't work the way you expect.

template < typename T >
void foo()
{
    if (is_same<T, SomeClass>::value) ...;
    else if (is_same<T, SomeClass2>::value) ...;
}

You can get is_same from std:: or boost:: depending on your desire/compiler. The former is only in C++0x.

The problem comes with what is in .... If you expect to be able to make some function call specific to those types within foo, you are sadly mistaken. A compiler error will result even though that section of code is never run when you pass in something that doesn't obey that expected interface.

To solve THAT problem you need to do something a bit different. I'd recommend tag dispatching:

struct v1_tag {};
struct v2_tag {};

template < typename T > struct someclass_version_tag;
template < > struct someclass_version_tag<SomeClass> { typedef v1_tag type; };
template < > struct someclass_version_tag<SomeClass2> { typedef v2_tag type; };

void foo(v1_tag) { ... }
void foo(v2_tag) { ... }
template < typename T > void foo()
{
    typedef typename someclass_version_tag<T>::type tag;
    foo(tag());
}

Note that you will not be suffering any runtime-polymorphism overhead here and with optimizations turned on it should result in the same or even smaller code size AND speed (though you shouldn't be worrying about that anyway until you've run a profiler).

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • Is it possible to return different types based on version_tag? – Boying Feb 24 '17 at 09:03
  • Sure. You need to come up with some way to query the information if you're stuck in C++03, or use auto in C++11 and up. So long as you don't try to return different types within the same instantiation you'll have no problem. – Edward Strange Feb 24 '17 at 17:45
  • 6
    now with C++17, you can use constexpr if statements to do this trivially – xaxxon Sep 08 '17 at 23:48
16

If you want to do something specific based on the type, specialize the template:

template <typename T>
void Foo() { }

template <>
void Foo<SomeClass>() { }

template <> 
void Foo<SomeClass2>() { }

// etc.

(You don't actually want to specialize the function template, though; this is for exposition only. You'll either want to overload the template if you can, or delegate to a specialized class template. For more on why and how to avoid specializing function templates, read Herb Sutter's Why Not Specialize Function Templates?)

James McNellis
  • 348,265
  • 75
  • 913
  • 977
4

No, however you can use partial specialization :

template<typename T>
struct Bar { static void foo(); };
template<typename T>
template<> inline void Bar<T>::foo() {
//generic
}
template<> inline void Bar<int>::foo() {
//stuff for int
}
template<> inline void Bar<QString>::foo() {
//QString
}

Edit Yes with type traits, however it's not really needed. Edit 2 type_traits example.

#include <type_traits>
template<typename T> void foo() {
    using std::is_same;
    if (is_same<T, T2>::value || is_same<T, T1>::value) { 
        /* stuff */
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • What if I want to do something like `if (is_same || is_same)`? –  Dec 23 '10 at 21:01
  • Then you have to use type traits `#include and std::is_same::value` if your compiler supports C++0x (VS2010/any recent gcc) or just use boost for old compiler compatibility. – OneOfOne Dec 23 '10 at 21:06
  • 3
    That's not partial specialization, it's full specialization. There's a pretty significant difference. Probably most significant is that it would be impossible to do partial specialization that way since it's not allowed with functions of any sort. – Edward Strange Dec 23 '10 at 21:22
3

Yes. You will have to use type traits. For example:

#include <boost/type_traits/is_same.hpp>

template <typename T>
void Foo ()
{
   if ((boost::is_same<T, SomeClass>::value))
      ...;
   else if ((boost::is_same<T, SomeClass2>::value))
      ...;
}

Depending on what you are trying to achieve, using template specialization might be much better choice.

Also, you can use enable_if/disable_if to conditionally enable/disable certain functions/methods. Combining this with type traits will allow, for example, using one function for one set of types and another function for another set of types.