c++ primer笔记9

1. Instantiation

A template is a blueprint; it is not itself a class or a function. The compiler uses the template to generate type-specific versions of the specified class or function. The process of generatng a type-specific instance of a template is known as instantiation. The term reflects the notion that a new “instance” of the template type or function is created.

A template is instantiated when we use it. A class template is instantiated when we refer to the an actual template class type, and a function template is instantiated when we call it or use it to initialize or assign to a pointer to function.

When we want to use a class template, we must always specify the template arguments explicitly. When we use a function template, the compiler will usually infer the template arguments for us.

2.Template Argument Deduction

To determine which functions to instantiate, the compiler looks at each argument. If the corresponding parameter was declared with a type that is a type parameter, then the compiler infers the type of the parameter from the type of the argument. The process of determining the types and values of the template arguments from the type of the function arguments is called template argument deduction. note: Multiple Type Parameter Arguments Must Match Exactly.

3.Limited Conversions on Type Parameter Arguments

short s1, s2;
int i1, i2;
compare(i1, i2); // ok: instantiate compare(int, int)
compare(s1, s2); // ok: instantiate compare(short, short)

In general, arguments are not converted to match an existing instantiation; instead, a new instance is generated. There are only two kinds of conversions that the compiler will perform rather than generating a new instantiation:

  • const conversions: A function that takes a reference or pointer to a const can be called with a reference or pointer to non const object, respectively, without generating a new instantiation. If the function takes a nonreference type, then const is ignored on either the parameter type or the argument. That is, the same instantiation will be used whether we pass a const or non const object to a function defined to take a nonreference type.
  • array or function to pointer conversions: If the template 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 treated as a pointer to its first element, and a function argument will be treated as a pointer to the function’s type.
template <typename T> T fobj(T, T); // arguments are copied
template <typename T>
T fref(const T&, const T&); // reference arguments
string s1("a value");
const string s2("another value");
fobj(s1, s2); // ok: calls f(string, string), const is ignored
fref(s1, s2); // ok: non const object s1 converted to const reference
int a[10], b[42];
fobj(a, b); // ok: calls f(int*, int*)
fref(a, b); // error: array types don't match; arguments aren't converted to pointers

4.Template Argument Deduction and Function Pointers

template <typename T> int compare(const T&, const T&);
// pf1 points to the instantiation int compare (const int&, const int&)
int (*pf1) (const int&, const int&) = compare;

It is an error if the template arguments cannot be determined from the function pointer type.

void func(int(*) (const string&, const string&));
void func(int(*) (const int&, const int&));
func(compare); // error: which instantiation of compare?

5.Function-Template Explicit Arguments

In some situations, it is not possible to deduce the types of the template arguments. In such situations, it is necessary to override the template argument deduction mechanism and explicitly specify the types or values to be used for the template
parameters.

Consider the following problem. We wish to define a function template called sum that takes arguments of two differnt types. We’d like the return type to be large enough to contain the sum of two values of any two types passed in any order. How can we do that? How should we specify sum’s return type?

// T or U as the returntype?
template <class T, class U> ??? sum(T, U);

// neither T nor U works as return typ
sum(3, 4L); // second type is larger; want U sum(T, U)
sum(3L, 4); // first type is larger; want T sum(T, U)

An alternative way to specify the return type is to introduce a third template parameter that must be explicitly specified by our caller. We supply an explicit template argument to a call in much the same way that we define an instance of a class template.

template <class T1, class T2, class T3>
T1 sum(T2, T3);

// ok T1 explicitly specified; T2 and T3 inferred from argument types
long val3 = sum<long>(i, lng); // ok: calls long sum(int, long)

Explicit template argument(s) are matched to corresponding template parameter(s) from left to right; the first template argument is matched to the first template parameter. An explicit template argument may be omitted only for the trailing (rightmost) parameters, assuming these can be deduced from the function parameters. If our sum function had been written as

template <class T1, class T2, class T3>
T3 alternative_sum(T2, T1);

// error: can't infer initial template parameters
long val3 = alternative_sum<long>(i, lng);
// ok: All three parameters explicitly specified
long val2 = alternative_sum<long, int, long>(i, lng);

Another example where explicit template arguments would be useful is the ambiguous program

template <typename T> int compare(const T&, const T&);
// overloaded versions of func; each take a different function pointer type
void func(int(*) (const string&, const string&));
void func(int(*) (const int&, const int&));
func(compare<int>); // ok: explicitly specify which version of compare

We saw that the arguments to the version of compare that has a single template type parameter must match exactly. If we wanted to call the function with compatible types, such as int and short, we could use an explicit template argument to specify either int or short as the parameter type. Write a program that uses the version of compare that has one template parameter. Call compare using an explicit template argument that will let you pass arguments of type int and short.

short sval = 123;
int ival = 2;
compare(static_cast<int>(sval), ival);
compare(static_cast<short>(ival), sval);
compare<int>(sval, ival);
compare<short>(sval, ival);