Modern C++:Efficient and Scalable Application Development
上QQ阅读APP看书,第一时间看更新

Defining templates

Returning back to the two versions of the maximum function, the routine is the same for both; all that has changed is the type. If you had a generic type, let's call it T, where T could be any type that implements an operator>, the routine could be described by this pseudocode:

    T maximum(T lhs, T rhs) 
{
return (lhs > rhs) ? lhs : rhs;
}

This will not compile because we have not defined the type T. Templates allow you to tell the compiler that the code uses a type and will be determined from the parameter passed to the function. The following code will compile:

    template<typename T> 
T maximum(T lhs, T rhs)
{
return (lhs > rhs) ? lhs : rhs;
}

The template declaration specifies the type that will be used using the typename identifier. The type T is a placeholder; you can use any name you like as long as it is not a name used elsewhere at the same scope, and of course, it must be used in the parameter list of the function. You can use class instead of typename, but the meaning is the same.

You can call this function, passing values of any type, and the compiler will create the code for that type, calling the operator> for that type.

It is important to realize that, the first time the compiler comes across a templated function, it will create a version of the function for the specified type. If you call the templated function for several different types, the compiler will create, or instantiate, a specialized function for each of these types.

The definition of this template indicates that only one type will be used, so you can only call it with two parameters of the same type:

    int i = maximum(1, 100);
double d = maximum(1.0, 100.0);
bool b = maximum(true, false);

All of these will compile and the first two will give the expected results. The last line will assign b to a value of true because bool is an integer and true has a value of 1+ and false has a value of 0. This may not be what you would want, so we will return to this issue later. Note that, since the template says that both parameters must be the same type, the following will not compile:

    int i = maximum(true, 100.99);

The reason is that the template parameter list only gives a single type. If you want to define a function with parameters of different types, then you will have to provide extra parameters to the template:

    template<typename T, typename U> 
T maximum(T lhs, U rhs)
{
return (lhs > rhs) ? lhs : rhs;
}
This is done to illustrate how templates work; it really does not make sense to define a maximum function that takes two different types.

This version is written for two different types, the template declaration mentions two types, and these are used for the two parameters. But notice that the function returns T, the type of the first parameter. The function can be called like this:

    cout << maximum(false, 100.99) << endl; // 1 
cout << maximum(100.99, false) << endl; // 100.99

The output from the first is 1 (or if you use the bool alpha manipulator, true) and the result of the second line is 100.99. The reason is not immediately obvious. In both cases, the comparison will return 100.99 from the function, but because the type of the return value is T, the returned value type will be the type of the first parameter. In the first case, 100.99 is first converted to a bool, and since 100.99 is not zero, the value returned is true (or 1). In the second case, the first parameter is a double, so the function returns a double and this means that 100.99 is returned. If the template version of maximum is changed to return U (the type of the second parameter) then the values returned by the preceding code are reversed: the first line returns 100.99 and the second returns 1.

Note that when you call the template function, you do not have to give the types of the template parameters because the compiler will deduce them. It is important to point out that this applies only to the parameters. The return type is not determined by the type of the variable the caller assigns to the function value because the function can be called without using the return value.

Although the compiler will deduce the template parameters from how you call the function, you can explicitly provide the types in the called function to call a specific version of the function and (if necessary) get the compiler to perform implicit conversions:

    // call template<typename T> maximum(T,T); 
int i = maximum<int>(false, 100.99);

This code will call the version of maximum that has two int parameters and returns an int, so the return value is 100, that is, 100.99 converted to an int.