Declaring And Using Function Templates

 < Day Day Up > 



Up until now, if you wanted to create different versions of the same function to operate on different data types you would overload the function. For instance, if you wanted to declare a function named Sum() that took two arguments, added them together, and returned the result you could create several versions of the function like so:

   int Sum(int val1, int val2); float Sum(float val1, float val2);   char Sum(char val1, char val2);

These three functions can be replaced with one function template. Example 15.1 gives the header file for the function template named Sum().

Listing 15.1: sumtemplate.h

start example
  1   #ifndef SUM_TEMPLATE_H  2   #define SUM_TEMPLATE_H  3  4   template<class T>  5   T Sum(T val1, T val2){  6        return val1 + val2;  7   }  8  9   #endif
end example

The Sum() function is declared to be a template by the keyword template appearing on line 4. Following the keyword template in angle brackets is the keyword class followed by a placeholder identifier named T. The class keyword as it is used here essentially means 'any type'. The placeholder T will then appear somewhere in the function. It can appear in more than one place, as it does here in the parameter list. You can use any valid identifier as a placeholder name, not just T. You can also declare more than one placeholder as you will see later.

Separating Declaration From Implementation: Some Background

Notice in example 15.1 that both the declaration of the Sum() function template and its definition appear in the header file. Older UNIX compilers based on AT&T's Cfront would allow you to split the declaration from the definition as we have been doing with ordinary functions throughout this book. However, splitting template declaration and definition had its problems and this method of doing business was largely abandoned by PC C++ compiler vendors in favor of grouping the declaration and definition of a class or function template in one header file. Eventually, some UNIX compiler vendors adopted this grouping practice as well. The ANSI C++ standard supports the separating approach but a lot of compiler vendors still only offer the grouping approach. So, in this chapter I will use the grouping approach as the examples should work for you regardless of your compiler.

When In Doubt Refer To Your Compiler Documentation

If you are using a brand new C++ compiler released within the last year you may be able to separate a template's declaration from its definition. Refer to your compiler's documentation to learn how this is done.

Example 15.1Continued

OK, with the background discussion complete let us continue with the Sum() function template example. To use the Sum() function template just include the header file as you would normally do. The function is called the same way as normal functions are called. Example 15.2 shows a main() function using the Sum() function on different data types.

Listing 15.2: main.cpp

start example
  1   #include <iostream>  2   #include "sumtemplate.h"  3   using namespace std;  4  5   int main(){  6       cout<<Sum(3, 25)<<endl;  7       cout<<Sum(3.456, 5.786)<<endl;  8       cout<<Sum('a', 'b')<<endl;  9       return 0; 10   }
end example

Figure 15-2 shows the results of running Example 15.2.


Figure 15-2: Results of Running Example 15.2

Using Multiple Placeholders

The Sum() function template declared and defined in example 15.1 used one type placeholder named T to reserve type spots in the function. Because both of the Sum() function's parameters are reserved with the same placeholder they must be of the same type when the function is called. To illustrate, let us see what happens when the Sum() function is called with an integer and a float argument as shown in the following line of code:

click to expand
Figure 15-3: Error Resulting from Calling Sum() with Two Different Type Arguments

When the Sum() function template is called with two different argument types an error results. This error was produced using the Macintosh version of Metrowerks CodeWarrior Release 5. One way to eliminate this error is to declare the Sum() function template to use two different placeholders. Example 15.3 shows the redeclared Sum() function.

Listing 15.3: sumtemplate.h

start example
  1    #ifndef SUM_TEMPLATE_H  2    #define SUM_TEMPLATE_H  3  4    template<class T, class U>  5    T Sum(T val1, U val2){  6         return val1 + val2;  7    }  8    #endif
end example

Notice now there are two type placeholders declared on line 4 of example 15.3, T and U. The U placeholder is used for the second parameter in the Sum() function while the T placeholder is used for the first parameter and the return value. This will solve one problem but introduce another. Example 15.4 shows the revised Sum() function template in use. Figure 15-4 shows the results of running Example 15.4.

Listing 15.4: main.cpp

start example
  1   #include <iostream>  2   #include "sumtemplate.h"  3   using namespace std;  4  5   int main(){  6       cout<<Sum(3, 25)<<endl;  7       cout<<Sum(3.456, 5.786)<<endl;  8       cout<<Sum('a', 'b')<<endl;  9       cout<<Sum(3, 3.5)<<endl; 10       return 0; 11   }
end example

click to expand
Figure 15-4: Results of Running Example 15.4

Refer to line 9 of example 15.4. Notice now that the Sum() function is called with the first argument an integer and the second argument a float. By using two placeholders in the function template the error produced by using two different argument types is eliminated. However, the result type of the Sum() function is dictated by the first argument type. Since the T placeholder is used to reserve the type spot for both the first parameter and the return type of the function, whatever type the first argument to the function happens to be will also be the return type of the function. In this example it is an integer. So, notice in figure 15-4 that the result of calling the Sum() function with the arguments 3 and 3.5 is 6, not 6.5! Notice what happens when the order of the arguments are swapped. Example 15.5 shows the revised main() function. Figure 15-5 shows the results of running example 15.5.

Example 15.5: main.cpp

start example

click to expand

end example

click to expand
Figure 15-5: Results of Running Example 15.5

To resolve the ambiguous return type issue simply declare yet another type place holder used specifically to dictate the function's return type. Example 15.6 gives the revised Sum() function with the extra type placeholder V declared and used to reserve the return type.

Listing 15.6: sumtemplate.h

start example
  1   #ifndef SUM_TEMPLATE_H  2   #define SUM_TEMPLATE_H  3  4   template<class T = int, class U = int, class V = int>  5   V Sum(T val1, U val2){  6        return val1 + val2;  7   }  8   #endif
end example

Now, you may be asking yourself, how the heck will the return type be set? Good question. Since the function only has two parameters it can only be called with two arguments. The only way out of this pickle is to use the special template definition syntax when you call the function. Oh, before I forget, notice on line 4 that the template parameters now have default types assigned. Example 15.7 gives the revised main() function showing how to call the function templates using the template syntax.

Listing 15.7: main.cpp

start example
  1   #include <iostream>  2   #include "sumtemplate.h"  3   using namespace std;    4  5   int main(){  6        cout<<Sum<int, int, int>(3, 25)<<endl;  7        cout<<Sum<double, double, double>(3.456, 5.786)<<endl;  8        cout<<Sum<char, char, char>('a', 'b')<<endl;  9        cout<<Sum<double, int, double>(3.5, 3)<<endl; 10        return 0; 11   }
end example

Notice the Sum() function call on line 6. The function name Sum is followed by a series of type names enclosed in angle brackets. This is referred to as a template specialization. The order of type names appearing in the specialization map to the same order as the function template type parameters. On line 6 the Sum() function is being specialized to take two integers as arguments to the function and return an integer value. Following the specialization is the argument list appearing in parentheses as usual.

Line 7 introduces another Sum() function specialization, as does line 8 and line 9. The specialization on line 9 says that the Sum() function will take a double as the first argument, an integer as the second argument, and return a double value.

Quick Review

Static polymorphism happens at compile time. Function templates are generic function definitions that are used by the compiler to construct specific function versions based on the types of arguments used to call the function. Function template declaration and definition should appear in the same header file unless your compiler supports separating the declaration of a function template from its definition. Check your compiler documentation.

Function templates are declared using the template keyword. Type placeholders are declared using the class keyword and can be any valid identifier. There can be more than one type placeholder declared and used in a function template. Because the compiler uses the function template to build a specific version of the function, using more than one type in a function call to a function having only one type placeholder can be problematic.

Use the template specialization syntax to specify placeholder types when the function template is called.



 < Day Day Up > 



C++ for Artists. The Art, Philosophy, and Science of Object-Oriented Programming
C++ For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504028
EAN: 2147483647
Year: 2003
Pages: 340
Authors: Rick Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net