Function Templates

Overloaded functions normally perform similar or identical operations on different types of data. If the operations are identical for each type, they can be expressed more compactly and conveniently using function templates. Initially, the programmer writes a single function-template definition. Based on the argument types provided explicitly or inferred from calls to this function, the compiler generates separate object-code functions (i.e., function-template specializations) to handle each function call appropriately. In C, this task can be performed using macros created with the preprocessor directive #define (see Appendix F, Preprocessor). However, macros can have serious side effects and do not enable the compiler to perform type checking. Function templates provide a compact solution, like macros, but enable full type checking.

Error-Prevention Tip 14.1

Function templates, like macros, enable software reuse. Unlike macros, function templates help eliminate many types of errors through the scrutiny of full C++ type checking.

All function-template definitions begin with keyword template followed by a list of template parameters to the function template enclosed in angle brackets (< and >); each template parameter that represents a type must be preceded by either of the interchangeable keywords class or typename, as in

template< typename T >

or

template< class ElementType >

or

template< typename BorderType, typename FillType >

The type template parameters of a function-template definition are used to specify the types of the arguments to the function, to specify the return type of the function and to declare variables within the function. The function definition follows and appears like any other function definition. Note that keywords typename and class used to specify function-template parameters actually mean "any built-in type or user-defined type."

Common Programming Error 14.1

Not placing keyword class or keyword typename before each type template parameter of a function template is a syntax error.

 

Example: Function Template printArray

Let us examine function template printArray in Fig. 14.1, lines 815. Function template printArray declares (line 8) a single template parameter T (T can be any valid identifier) for the type of the array to be printed by function printArray; T is referred to as a type template parameter, or type parameter. You will see nontype template parameters in Section 14.5.

Figure 14.1. Function-template specializations of function template printArray.

(This item is displayed on pages 752 - 753 in the print version)

1 // Fig. 14.1: fig14_01.cpp 2 // Using template functions. 3 #include 4 using std::cout; 5 using std::endl; 6 7 // function template printArray definition 8 template< typename T > 9 void printArray( const T *array, int count ) 10 { 11 for ( int i = 0; i < count; i++ ) 12 cout << array[ i ] << " "; 13 14 cout << endl; 15 } // end function template printArray 16 17 int main() 18 { 19 const int ACOUNT = 5; // size of array a 20 const int BCOUNT = 7; // size of array b 21 const int CCOUNT = 6; // size of array c 22 23 int a[ ACOUNT ] = { 1, 2, 3, 4, 5 }; 24 double b[ BCOUNT ] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 }; 25 char c[ CCOUNT ] = "HELLO"; // 6th position for null 26 27 cout << "Array a contains:" << endl; 28 29 // call integer function-template specialization 30 printArray( a, ACOUNT ); 31 32 cout << "Array b contains:" << endl; 33 34 // call double function-template specialization 35 printArray( b, BCOUNT ); 36 37 cout << "Array c contains:" << endl; 38 39 // call character function-template specialization 40 printArray( c, CCOUNT ); 41 return 0; 42 } // end main  

Array a contains: 1 2 3 4 5 Array b contains: 1.1 2.2 3.3 4.4 5.5 6.6 7.7 Array c contains: H E L L O  

When the compiler detects a printArray function invocation in the client program (e.g., lines 30, 35 and 40), the compiler uses its overload resolution capabilities to find a definition of function printArray that best matches the function call. In this case, the only printArray function with the appropriate number of parameters is the printArray function template (lines 815). Consider the function call at line 30. The compiler compares the type of printArray's first argument (int * at line 30) to the printArray function template's first parameter (const T * at line 9) and deduces that replacing the type parameter T with int would make the argument match the parameter. Then, the compiler substitutes int for T throughout the template definition and compiles a printArray specialization that can display an array of int values. In Fig. 14.1, the compiler creates three printArray specializationsone that expects an int array, one that expects a double array and one that expects a char array. For example, the function-template specialization for type int is


void printArray( const int *array, int count ) { for ( int i = 0; i < count; i++ ) cout << array[ i ] << " "; cout << endl; } // end function printArray

The name of a template parameter can be declared only once in the template parameter list of a template header but can be used repeatedly in the function's header and body. Template parameter names among function templates need not be unique.


Figure 14.1 demonstrates function template printArray (lines 815). The program begins by declaring five-element int array a, seven-element double array b and six-element char array c (lines 2325, respectively). Then, the program outputs each array by calling printArrayonce with a first argument a of type int * (line 30), once with a first argument b of type double * (line 35) and once with a first argument c of type char * (line 40). The call in line 30, for example, causes the compiler to infer that T is int and to instantiate a printArray function-template specialization, for which type parameter T is int. The call in line 35 causes the compiler to infer that T is double and to instantiate a second printArray function-template specialization, for which type parameter T is double. The call in line 40 causes the compiler to infer that T is char and to instantiate a third printArray function-template specialization, for which type parameter T is char. It is important to note that if T (line 8) represents a user-defined type (which it does not in Fig. 14.1), there must be an overloaded stream insertion operator for that type; otherwise, the first stream insertion operator in line 12 will not compile.

Common Programming Error 14.2

If a template is invoked with a user-defined type, and if that template uses functions or operators (e.g., ==, +, <=) with objects of that class type, then those functions and operators must be overloaded for the user-defined type. Forgetting to overload such operators causes compilation errors.

In this example, the template mechanism saves the programmer from having to write three separate overloaded functions with prototypes

void printArray( const int *, int ); void printArray( const double *, int ); void printArray( const char *, int );

that all use the same code, except for type T (as used in line 9).


Performance Tip 14.1

Although templates offer software-reusability benefits, remember that multiple function-template specializations and class-template specializations are instantiated in a program (at compile time), despite the fact that the template is written only once. These copies can consume considerable memory. This is not normally an issue, though, because the code generated by the template is the same size as the code the programmer would have written to produce the separate overloaded functions.

Категории