I l @ ve RuBoard |
Templates provide C++'s most powerful form of genericity. They allow you to write generic code that works with many kinds of unrelated objects ”for example, strings that contain various kinds of characters , containers that can hold arbitrary types of objects, and algorithms that can operate on arbitrary types of sequences.
Template specialization lets templates deal with special cases. Sometimes, a generic algorithm can work much more efficiently for a certain kind of sequence (for example, when given random-access iterators), so it makes sense to specialize it for that case while using the slower but more generic approach for all other cases. Performance is a common reason to specialize, but it's not the only one. For example, you might also specialize a template to work with certain objects that don't conform to the normal interface expected by the generic template. These special cases can be handled using two forms of template specialization: explicit specialization and partial specialization. Explicit SpecializationExplicit specialization lets you write a specific implementation for a particular combination of template parameters. For example, consider the following function template: template<typename T> void sort( Array<T>& v ) { /*...*/ }; If we have a faster (or other specialized) way we want to deal with Array s of char* 's, we could explicitly specialize sort() for that case: template<> void sort<char*>( Array<char*>& ); The compiler will then choose the most appropriate template. For example: Array<int> ai; Array<char*> apc; sort( ai ); // calls sort<int> sort( apc ); // calls specialized sort<char*> Partial Specialization
For class templates only, you can define partial specializations that don't have to fix all the primary (unspecialized) class template's parameters. Here is an example from the C++ standard [C++98], in 14.5.4 [temp.class.spec]. The first template is the primary class template: template<typename T1, typename T2, int I> class A { }; // #1 We can specialize this for the case when T2 is a T1* : template<typename T, int I> class A<T, T*, I> { }; // #2 Or for the case when T1 is any pointer: template<typename T1, typename T2, int I> class A<T1*, T2, I> { }; // #3 Or for the case when T1 is int and T2 is any pointer and I is 5: template<typename T> class A<int, T*, 5> { }; // #4 Or for the case when T2 is any pointer: template<typename T1, typename T2, int I> class A<T1, T2*, I> { }; // #5 Declarations 2 through 5 declare partial specializations of the primary template. The compiler will then choose the appropriate template. From [C++98] section 14.5.4.1 comes the following: A<int, int, 1> a1; // uses #1 A<int, int*, 1> a2; // uses #2, T is int, // I is 1 A<int, char*, 5> a3; // uses #4, T is char A<int, char*, 1> a4; // uses #5, T1 is int, // T2 is char, // I is 1 A<int*, int*, 2> a5; // ambiguous: // matches #3 and #5 Function Template OverloadingNow let's consider function template overloading. It isn't the same as specialization, but it's related to it. C++ lets you overload functions, yet makes sure the right one is called: int f( int ); long f( double ); int i; double d; f( i ); // calls f(int) f( d ); // calls f(double) Similarly, you can also overload function templates, which brings us to the final question:
template<typename T1, typename T2> void g( T1, T2 ); // 1 template<typename T> void g( T ); // 2 template<typename T> void g( T, T ); // 3 template<typename T> void g( T* ); // 4 template<typename T> void g( T*, T ); // 5 template<typename T> void g( T, T* ); // 6 template<typename T> void g( int, T* ); // 7 template<> void g<int>( int ); // 8 void g( int, double ); // 9 void g( int ); // 10 First, let's simplify things a little by noticing that there are two groups of overloaded g() 's here: those that take a single parameter and those that take two parameters. template<typename T1, typename T2> void g( T1, T2 ); // 1 template<typename T> void g( T, T ); // 3 template<typename T> void g( T*, T ); // 5 template<typename T> void g( T, T* ); // 6 template<typename T> void g( int, T* ); // 7 void g( int, double ); // 9 template<typename T> void g( T ); // 2 template<typename T> void g( T* ); // 4 template<> void g<int>( int ); // 8 void g( int ); // 10 Note that I deliberately didn't muddy the waters by including an overload with two parameters where the second parameter had a default. Had there been such a function, then for the purposes of determining the correct ordering, it should be considered in both lists ”once as a single-parameter function (using the default), and once as a two-parameter function (not using the default). Now let's consider each of the calls in turn : Which of the above functions are invoked by each of the following? Identify the template parameter types, where appropriate. int i; double d; float f; complex<double> c; g( i ); // a
The good news is that modern compilers generally have good template support so that you can make use of features like the above more reliably and portably than in the past. The (potential) bad news is that if you got all the answers right, you may know the rules better than your current compiler does. |
I l @ ve RuBoard |