5

In a function within a template class, I'm trying to distinguish between primitive types and others.

In c++ 11 you can do:

if(std::is_fundamental<T>::value)
{
    // Treat it as a primitive
}
else
{
    //Treat it otherwise
}

Please correct me if I'm wrong and this is not only in c++ 11.

Is there an alternative to this in earlier versions of c++?

Subway
  • 5,286
  • 11
  • 48
  • 59

2 Answers2

8

You could use Boost's type traits in C++03 like this:

#include  <boost/type_traits/is_fundamental.hpp>

...

if(boost::is_fundamental<T>::value)
{
    // Treat it as a primitive
}
else
{
    //Treat it otherwise
}

I guess this should work for C++98 as well.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
djf
  • 6,592
  • 6
  • 44
  • 62
2

With this code you will likly have trouble. If you need to distinguish between different type traits this must be done at compile time, not at run time. Depending on what operations you are performing one of the two branches of your if may not compile. So it is better to forward to an specialized function:

void operation_impl(boost::true_type /*other params*/) {
  // Treat it is primitive 
}

void operation_impl(boost::false_type /*other params*/) {
  // Treat it otherwise
}

template<class T>
void operation(/* params*/) {
  operation_impl(boost::is_fundamental<T>::type() /*other params*/);
}

With this implementation technique only the used branch needs to compile (i.e. be correct).

Edit:

Here are some additional informations. The solution to this problem have to do with instanciation of templates. I switch from is_fundamental to is_array to show how operations may fail.

Lets start with the first example:

template <class T>
void fun(T t) {
    if(boost::is_array<T>::value)
    {
        std::cout << "true" << std::endl;
    }
    else
    {
        std::cout << "false" << std::endl;
    }
}

void f(int i) {
    fun(i);
}

It will compile and run and the compiler will see that only one branch of the if statement will be used and will remove the other as unused code.

In my second example I will do someithing in the case I use an array operation:

template<class T>
void fun(T& t) {
    if(boost::is_array<T>::value)
    {
        std::cout << t[0];
    }
    else
    {
        std::cout << t;
    }
}

void f(int i) {
    fun(i);
}

Now it will not compile. The reason is with the int as an template argument t[0]is ill formed. You cant use this runtime statement to distinguish on type properties at compile time which are needed in your code (in this example the property of beeing an array and the use of t[0]).

In the third example we will disitinguish on compile time via function overloading:

template<class T>
void fun_impl(boost::true_type, T& t) {
    std::cout << t[0];
}

template<class T>
void fun_impl(boost::false_type, T& t) {
    std::cout << t;
}

template<class T>
void fun(T& t) {
    fun_impl(typename boost::is_array<T>::type(),t);
}

void f(int i) {
    fun(i);
}

Here is_array<T>::type is either true_type or false_type. This result is used as a selector for choosing the right overload of fun_impl at compile time and only the choosen overload is instanziated and compiled.

Normaly such techniques are used to select at comopile time a best implementation which may be only compilable if the types have certain properties.

2nd edit:

This will of course change if static if is part of the language.

Jan Herrmann
  • 2,717
  • 17
  • 21
  • could you please elaborate about the problem I might encounter with the other approach? Or can you add a reference to an explanation? I didn't understand what you wrote. – Subway May 30 '13 at 11:58
  • Thank you! +1 for the elaboration. I still have a question though: As I understand, your point is to get the code compiled and in addition to save compilation of unnecessary code. Given that my code compiles, are there actual trouble (like an unexpected behavior) I might encounter at run time using the first approach? – Subway May 30 '13 at 13:19
  • @Subway no I think the compiler sees your boost::is_fundamental::value as a constant and will remove unused code, so there is no runtime overhead and no UB. The approach is to get it compiled at all. The const value member is for using with std::enable_if (as an integral template argument) and of course for demonstration (like printing the value). I think for every other purpose you should take the tour via function overloading. – Jan Herrmann May 30 '13 at 13:35