So you have just created a sweet little template function that will save the world from global warming. Quite an amazing feat! You have decided declare the function like so:
template <typename T>
void SaveTheWorld(T moreOzone) { … }
The problem is that the function has to be called many times, with many different template parameter types for it to be effective, so you need to make sure it is as efficient as possible. Since some of the template parameter types might be large objects, passing by value may not be a good idea. You decide to redefine SaveTheWorld like so:
template <typename T>
void SaveTheWorld(const T& moreOzone) { … }
It turns out that this is going to perform a lot better than the previous definition, but for primitive types like ints, and chars, passing by value turns out to be a little quicker, and our secret ozone object needs a lot of those little guys. Wouldn’t it be nice if primitives could use the top one, and class types defined by the program use the bottom one? Well with some creative use of C++ templates, it turns out we can. The following ideas were taken from the book “C++ Templates, The Complete Guide” by David Vandevoorde and Nicolai M. Josuttis. I recommend this read for anyone who wants a serious understanding of C++ Templates.
The first thing we need is a template that takes as template parameters a boolean value and two types. It will contain a typedef called BestType, which will be of type T1 if the boolean is true, and T2 if the boolean is false. To do this we will also need to utilize partial specialization.
// primary template
template <bool B, typename T1, typename T2>
class TypeChooser;
// partial specialization for true
template<typename T1, typename T2>
class TypeChooser<true, T1, T2> {
public:
typedef T1 BestType;
};
// partial specialization for false
template<typename T1, typename T2>
class TypeChooser<false, T1, T2> {
public:
typedef T2 BestType;
};
With these definitions, TypeChooser<true, int, char>::BestType will be type int, and TypeChooser<false, int, char>::BestType will be type char.
Now we need a class template that will tell us whether a type is a class or not. Here is the code for that:
template <typename T>
class IsClass {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int (C::*pmt));
template<typename C> static Two test(…);
public:
enum { Yes = sizeof(IsClass<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
This class might be a little confusing, so I’ll explain it here. If you declare IsClass with an integer type like IsClass<int>, then IsClass<int>::Yes is going to be false. The reason is because the declaration test(int (int::*pmt)) would be illegal, so the compiler will choose the test(…) function when it comes across IsClass<int>::test<int>(0), and return Two, whose size is 2. However, test(int (MyClass::*pmt)) would be legal, with int (MyClass::*pmt) being of type pointer to member function of MyClass. The following demonstrates the usage of this class.
if(IsClass<int>::No) {
cout << “NOT A CLASS!” << endl;
}
if(IsClass<MyClass>::Yes) {
cout << “WE HAVE A CLASS!” << endl;
}
Finally, we’ll create a template class called ParamTChooser who will choose the correct parameter type for our SaveTheWorld function.
template <typename T>
class ParamTChooser {
public:
typedef typename
TypeChooser<IsClass<T>::No, T, const T&>::BestType Type;
};
So ParamTChooser<T>::Type will be of type T if T is a primitive, and of type const T& if T is not a primitive. Finally, we redeclare SaveTheWorld as follows:
template <typename T>
void SaveTheWorld(typename ParamTChooser<T>::Type moreOzone);
Now when we do SaveTheWorld<int>(myInt), it will pass myInt by value, and when we do SaveTheWorld<SomeClass>(myClass), it will pass myClass by const reference.