16.2.1. Conversions and Template Type Parameters

Image

As with a nontemplate function, the arguments we pass in a call to a function template are used to initialize that function’s parameters. Function parameters whose type uses a template type parameter have special initialization rules. Only a very limited number of conversions are automatically applied to such arguments. Rather than converting the arguments, the compiler generates a new instantiation.

As usual, top-level consts (§ 2.4.3, p. 63) in either the parameter or the argument are ignored. The only other conversions performed in a call to a function template are

const conversions: A function parameter that is a reference (or pointer) to a const can be passed a reference (or pointer) to a nonconst object (§ 4.11.2, p. 162).

• Array- or function-to-pointer conversions: If the function parameter is not a reference type, then the normal pointer conversion will be applied to arguments of array or function type. An array argument will be converted to a pointer to its first element. Similarly, a function argument will be converted to a pointer to the function’s type (§ 4.11.2, p. 161).

Other conversions, such as the arithmetic conversions (§ 4.11.1, p. 159), derived-to-base (§ 15.2.2, p. 597), and user-defined conversions (§ 7.5.4, p. 294, and § 14.9, p. 579), are not performed.

As examples, consider calls to the functions fobj and fref. The fobj function copies its parameters, whereas fref’s parameters are references:

template <typename T> T fobj(T, T); // arguments are copied
template <typename T> T fref(const T&, const T&); // references
string s1("a value");
const string s2("another value");
fobj(s1, s2); // calls fobj(string, string); const is ignored
fref(s1, s2); // calls fref(const string&, const string&)
              // uses premissible conversion to const on s1
int a[10], b[42];
fobj(a, b); // calls f(int*, int*)
fref(a, b); // error: array types don't match

In the first pair of calls, we pass a string and a const string. Even though these types do not match exactly, both calls are legal. In the call to fobj, the arguments are copied, so whether the original object is const doesn’t matter. In the call to fref, the parameter type is a reference to const. Conversion to const for a reference parameter is a permitted conversion, so this call is legal.

In the next pair of calls, we pass array arguments in which the arrays are different sizes and hence have different types. In the call to fobj, the fact that the array types differ doesn’t matter. Both arrays are converted to pointers. The template parameter type in fobj is int*. The call to fref, however, is illegal. When the parameter is a reference, the arrays are not converted to pointers (§ 6.2.4, p. 217). The types of a and b don’t match, so the call is in error.


Image Note

const conversions and array or function to pointer are the only automatic conversions for arguments to parameters with template types.


Function Parameters That Use the Same Template Parameter Type

A template type parameter can be used as the type of more than one function parameter. Because there are limited conversions, the arguments to such parameters must have essentially the same type. If the deduced types do not match, then the call is an error. For example, our compare function (§ 16.1.1, p. 652) takes two const T& parameters. Its arguments must have essentially the same type:

long lng;
compare(lng, 1024); // error: cannot instantiate compare(long, int)

This call is in error because the arguments to compare don’t have the same type. The template argument deduced from the first argument is long; the one for the second is int. These types don’t match, so template argument deduction fails.

If we want to allow normal conversions on the arguments, we can define the function with two type parameters:

// argument types can differ but must be compatible
template <typename A, typename B>
int flexibleCompare(const A& v1, const B& v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}

Now the user may supply arguments of different types:

long lng;
flexibleCompare(lng, 1024); // ok: calls flexibleCompare(long, int)

Of course, a < operator must exist that can compare values of those types.

Normal Conversions Apply for Ordinary Arguments

A function template can have parameters that are defined using ordinary types—that is, types that do not involve a template type parameter. Such arguments have no special processing; they are converted as usual to the corresponding type of the parameter (§ 6.1, p. 203). For example, consider the following template:

template <typename T> ostream &print(ostream &os, const T &obj)
{
    return os << obj;
}

The first function parameter has a known type, ostream&. The second parameter, obj, has a template parameter type. Because the type of os is fixed, normal conversions are applied to arguments passed to os when print is called:

print(cout, 42); // instantiates print(ostream&, int)
ofstream f("output");
print(f, 10);    // uses print(ostream&, int); converts f to ostream&

In the first call, the type of the first argument exactly matches the type of the first parameter. This call will cause a version of print that takes an ostream& and an int to be instantiated. In the second call, the first argument is an ofstream and there is a conversion from ofstream to ostream&8.2.1, p. 317). Because the type of this parameter does not depend on a template parameter, the compiler will implicitly convert f to ostream&.


Image Note

Normal conversions are applied to arguments whose type is not a template parameter.



Exercises Section 16.2.1

Exercise 16.32: What happens during template argument deduction?

Exercise 16.33: Name two type conversions allowed on function arguments involved in template argument deduction.

Exercise 16.34: Given only the following code, explain whether each of these calls is legal. If so, what is the type of T? If not, why not?

template <class T> int compare(const T&, const T&);

(a) compare("hi", "world");

(b) compare("bye", "dad");

Exercise 16.35: Which, if any, of the following calls are errors? If the call is legal, what is the type of T? If the call is not legal, what is the problem?

template <typename T> T calc(T, int);
template <typename T> T fcn(T, T);
double d;    float f;    char c;

(a) calc(c, 'c'),

(b) calc(d, f);

(c) fcn(c, 'c'),

(d) fcn(d, f);

Exercise 16.36: What happens in the following calls:

template <typename T> f1(T, T);
template <typename T1, typename T2) f2(T1, T2);
int i = 0, j = 42, *p1 = &i, *p2 = &j;
const int *cp1 = &i, *cp2 = &j;

(a) f1(p1, p2);

(b) f2(p1, p2);

(c) f1(cp1, cp2);

(d) f2(cp1, cp2);

(e) f1(p1, cp1);

(f) f2(p1, cp1);


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.198.174