I l@ve RuBoard Previous Section Next Section

2.5 Type-to-Type Mapping

As Section 2.2 mentions, partial specialization for template functions does not exist. At times, however, you might need to simulate similar functionality. Consider the following function.



template <class T, class U>


T* Create(const U& arg)


{


   return new T(arg);


}


Create makes a new object, passing an argument to its constructor.

Now say there is a rule in your application: Objects of type Widget are untouchable legacy code and must take two arguments upon construction, the second being a fixed value such as -1. Your own classes, derived from Widget, don't have this problem.

How can you specialize Create so that it treats Widget differently from all other types? An obvious solution is to make a separate CreateWidget function that copes with the particular case. Unfortunately, now you don't have a uniform interface for creating Widgets and objects derived from Widget. This renders Create unusable in any generic code.

You cannot partially specialize a function; that is, you can't write something like this:



// Illegal code — don't try this at home


template <class U>


Widget* Create<Widget, U>(const U& arg)


{


   return new Widget(arg, -1);


}


In the absence of partial specialization of functions, the only tool available is, again, overloading. A solution would be to pass a dummy object of type T and rely on overloading:



template <class T, class U>


T* Create(const U& arg, T /* dummy */)


{


   return new T(arg);


}


template <class U>


Widget* Create(const U& arg, Widget /* dummy */)


{


   return new Widget(arg, -1);


}


Such a solution would incur the overhead of constructing an arbitrarily complex object that remains unused. We need a light vehicle for transporting the type information about T to Create. This is the role of Type2Type: It is a type's representative, a light identifier that you can pass to overloaded functions.

The definition of Type2Type is as follows.



template <typename T>


struct Type2Type


{


   typedef T OriginalType;


};


Type2Type is devoid of any value, but distinct types lead to distinct Type2Type instantiations, which is what we need.

Now you can write the following:



// An implementation of Create relying on overloading


//      and Type2Type


template <class T, class U>


T* Create(const U& arg, Type2Type<T>)


{


   return new T(arg);


}


template <class U>


Widget* Create(const U& arg, Type2Type<Widget>)


{


   return new Widget(arg, -1);


}


// Use Create()


String* pStr = Create("Hello", Type2Type<String>());


Widget* pW = Create(100, Type2Type<Widget>());


The second parameter of Create serves only to select the appropriate overload. Now you can specialize Create for various instantiations of Type2Type, which you map to various types in your application.

    I l@ve RuBoard Previous Section Next Section