Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to do an if else depending type of type in c++ template? [duplicate]

Tags:

c++

templates

You could use typeid:

if (typeid(T) == typeid(int))

Or you could use the std::is_same type trait:

if (std::is_same<T, int>::value)

What you want is probably something like a compile-time if. Unfortunately, C++11 has no native support for such a language construct.

However, if you just want to check whether two types are identical, the std::is_same<> type trait should help you:

#include <type_traits> // <== INCLUDE THIS STANDARD HEADER

// class template:
template <class T>
class mycontainer 
{
    T element;
public:
    mycontainer (T arg) {element=arg;}
    T increase () 
    {
        if (std::is_same<T, int>::value)   // <== THIS IS HOW YOU WOULD USE IT
            return ++element;

        if (std::is_same<T, char>::value)  // <== THIS IS HOW YOU WOULD USE IT
        {
            if ((element>='a') && (element<='z'))
                element+='A'-'a';
        }

        return element;
    }
};

However, keep in mind that the condition is evaluated at run-time, even though the value of is_same<T, int>::value is known at compile-time. This means that both the true and the false branch of the if statement must compile!

For instance, the following would not be legal:

if (std::is_same<T, int>::value)
{
    cout << element;
}
else if (std::is_same<T, my_class>::value)
{
    element->print();  // Would not compile when T is int!
}

Also, as Xeo correctly pointed out in the comments, the compiler will likely issue warnings because your condition will always evaluate to true or to false, so one of the two branches will contain unreachable code.


You may use explicit template specialization

#include <iostream>
using namespace std;

// class template:
template <class T>
class mycontainer {
    T element;
  public:
    mycontainer (T arg) {element=arg;}
    T increase();
};


template<>
int mycontainer<int>::increase(){
    return ++element;
}

template<>
char mycontainer<char>::increase(){
    if ((element>='a')&&(element<='z'))
       element+='A'-'a';
    return element;
}

int main(){
        mycontainer<int> A(10);
        mycontainer<char> B('x');

        cout << A.increase() <<endl;
        cout << B.increase() <<endl;
        return 0;
}

How about a simple overload?

// in the private section
static int& do_increase(int& i){ return ++i; }
static char& do_increase(char& c){
  if(c >= 'a' && c <= 'z')
    c += 'A' - 'a';
  return c;
}
template<class U>
static U& do_increase(U& arg){
  // some default implementation?
  return arg;
}

(Note that the standard doesn't guarantee alphabetic order for the numeric values of a char.)

Then simply call that in increase as return do_increase(element);.


The usual solution here is to forward to an overloaded function with an additional argument. Something like:

template <typename T>
class MyContainer
{
    T increase( int const* ) { /* special treatment for int */ }
    T increase( ... )        { /* default treatment         */ }
public:
    T increase()
    {
        return increase( (T const*)0 );
    }
};

With a little imagination, you can come up with all sorts of distinctions. If you make the target functions with the extra arguments templates, you can even leverage off SFINAE: design the dummy argument so that template type substitution fails, and the function will not be considered in the overload set. And since all of the functions are inline, it's probable that there will be no extra overhead, provided that you optimize.