2.1 A First Look at Function Templates

Ru-Brd

Function templates provide a functional behavior that can be called for different types. In other words, a function template represents a family of functions. The representation looks a lot like an ordinary function, except that some elements of the function are left undetermined: These elements are parameterized. To illustrate , let's look at a simple example.

2.1.1 Defining the Template

The following is a function template that returns the maximum of two values:

  // basics/max.hpp  template <typename T>  inline T const& max (T const& a, T const& b)  {  // if  a  <  b  then use  b  else use  a      return a<b?b:a;  } 

This template definition specifies a family of functions that returns the maximum of two values, which are passed as function parameters a and b . The type of these parameters is left open as template parameter T . As seen in this example, template parameters must be announced with syntax of the following form:

 template <  comma-separated-list-of-parameters  > 

In our example, the list of parameters is typename T . Note how the less-than and the greater-than symbols are used as brackets; we refer to these as angle brackets . The keyword typename introduces a so-called type parameter . This is by far the most common kind of template parameter in C++ programs, but other parameters are possible, and we discuss them later (see Chapter 4).

Here, the type parameter is T . You can use any identifier as a parameter name , but using T is the convention. The type parameter represents an arbitrary type that is specified by the caller when the caller calls the function. You can use any type (fundamental type, class, and so on) as long as it provides the operations that the template uses. In this case, type T has to support operator < because a and b are compared using this operator.

For historical reasons, you can also use class instead of typename to define a type parameter. The keyword typename came relatively late in the evolution of the C++ language. Prior to that, the keyword class was the only way to introduce a type parameter, and this remains a valid way to do so. Hence, the template max() could be defined equivalently as follows :

 template <class T>  inline T const& max (T const& a, T const& b)  {  // if  a  <  b  then use  b  else use  a      return a<b?b:a;  } 

Semantically there is no difference in this context. So, even if you use class here, any type may be used for template arguments. However, because this use of class can be misleading (not only class types can be substituted for T ), you should prefer the use of typename in this context. Note also that unlike class type declarations, the keyword struct cannot be used in place of typename when declaring type parameters.

2.1.2 Using the Template

The following program shows how to use the max() function template:

  // basics/max.cpp  #include <iostream>  #include <string>  #include "max.hpp"  int main()  {      int i = 42;      std::cout << "max(7,i): " << ::max(7,i) << std::endl;      double f1 = 3.4;      double f2 = -6.7;      std::cout << "max(f1,f2): " << ::max(f1,f2) << std::endl;      std::string s1 = "mathematics";      std::string s2 = "math";      std::cout << "max(s1,s2): " << ::max(s1,s2) << std::endl;  } 

Inside the program, max() is called three times: once for two int s, once for two double s, and once for two std::string s. Each time, the maximum is computed. As a result, the program has the following output:

 max(7,i):   42  max(f1,f2): 3.4  max(s1,s2): mathematics 

Note that each call of the max() template is qualified with :: . This is to make sure that our max() template is found in the global namespace. There is also an std::max() template in the standard library, which under some circumstances may be called or may lead to ambiguity. [1]

[1] For example, if one argument type is defined in namespace std (such as strings), according to the lookup rules of C++, both the global and the std max() template are found.

Normally, templates aren't compiled into single entities that can handle any type. Instead, different entities are generated from the template for every type for which the template is used. [2] Thus, max() is compiled for each of these three types. For example, the first call of max()

[2] The "one-entity-fits-all" alternative is conceivable but rare in practice. All language rules are based on the concept that different entities are generated.

 int i = 42;   max(7,i)   

uses the function template with int as template parameter T . Thus, it has the semantics of calling the following code:

 inline int const& max (int const& a, int const& b)  {  // if  a  <  b  then use  b  else use  a      return a<b?b:a;  } 

The process of replacing template parameters by concrete types is called instantiation . It results in an instance of a template. Unfortunately, the terms instance and instantiate are used in a different context in object-oriented programming ”namely, for a concrete object of a class. However, because this book is about templates, we use this term for the "use" of templates unless otherwise specified.

Note that the mere use of a function template can trigger such an instantiation process. There is no need for the programmer to request the instantiation separately.

Similarly, the other calls of max() instantiate the max template for double and std::string as if they were declared and implemented individually:

 const double& max (double const&, double const&);  const std::string& max (std::string const&, std::string const&); 

An attempt to instantiate a template for a type that doesn't support all the operations used within it will result in a compile-time error. For example:

 std::complex<float> c1, c2;  // doesn't provide operator <    max(c1,c2);  // ERROR at compile time  

Thus, templates are compiled twice:

  1. Without instantiation, the template code itself is checked for correct syntax. Syntax errors are discovered , such as missing semicolons.

  2. At the time of instantiation, the template code is checked to ensure that all calls are valid. Invalid calls are discovered, such as unsupported function calls.

This leads to an important problem in the handling of templates in practice: When a function template is used in a way that triggers its instantiation, a compiler will (at some point) need to see that template's definition. This breaks the usual compile and link distinction for ordinary functions, when the declaration of a function is sufficient to compile its use. Methods of handling this problem are discussed in Chapter 6. For the moment, let's take the simplest approach: Each template is implemented inside a header file by using inline functions.

Ru-Brd


C++ Templates
C++ Templates: The Complete Guide
ISBN: 0201734842
EAN: 2147483647
Year: 2002
Pages: 185

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