11.1 The Deduction Process

Ru-Brd

11.1 The Deduction Process

The deduction process compares the types of an argument of a function call with the corresponding parameterized type of a function template and attempts to conclude the correct substitution for one or more of the deduced parameters. Each argument-parameter pair is analyzed independently, and if the conclusions differ in the end, the deduction process fails. Consider the following example:

 template<typename T>  T const& max (T const& a, T const& b)  {      return a<b?b:a;  }  int g = max(1, 1.0); 

Here the first call argument is of type int so the parameter T of our original max() template is tentatively deduced to be int . The second call argument is a double , however, and so T should be double for this argument: This conflicts with the previous conclusion. Note that we say that "the deduction process fails," not that "the program is invalid." After all, it is possible that the deduction process would succeed for another template named max (function templates can be overloaded much like ordinary functions; see Section 2.4 on page 15 and Chapter 12).

If all the deduced template parameters are consistently determined, the deduction process can still fail if substituting the arguments in the rest of the function declaration results in an invalid construct. For example:

 template<typename T>  typename T::ElementT at (T const& a, int i)  {      return a[i];  }  void f (int* p)  {      int x = at(p, 7);  } 

Here T is concluded to be int* (there is only one parameter type where T appears, so there are obviously no analysis conflicts). However, substituting int* for T in the return type T::ElementT is clearly invalid C++, and the deduction process fails. [1] The error message is likely to say that no match was found for the call to at() . In contrast, if all the template arguments are mentioned explicitly, then there is no chance that the deduction process will succeed for another template, and the error message is more likely to say that the template arguments for at() are invalid. You can investigate this by comparing the diagnostic for the previous example with

[1] In this case, deduction failure leads to an error. However, this falls under the SFINAE principle (see Section 8.3.1 on page 106): If there were another function for which deduction succeeds, the code could be valid.

 void f (int* p)  {      int x = at<int*>(p, 7);  } 

on your favorite C++ implementation.

We still need to explore how argument-parameter matching proceeds. We describe it in terms of matching a type A (derived from the argument type) to a parameterized type P (derived from the parameter declaration). If the parameter is declared with a reference declarator, P is taken to be the type referenced, and A is the type of the argument. Otherwise, however, P is the declared parameter type, and A is obtained from the type of the argument by decaying [2] array and function types to pointer types, ignoring top-level const and volatile qualifiers. For example:

[2] Decay is the term used to refer to the implicit conversion of function and array types to pointer types.

 template<typename T> void f(T);  //Pis  T  template<typename T> void g(T&);  // P is also  T  double x[20];  int const seven = 7;  f(x);  // nonreference parameter:  T  is  double*  g(x);  // reference parameter:  T  is  double[20]  f(seven);  // nonreference parameter:  T  is  int  g(seven);  // reference parameter:  T  is  int const  f(7);  // nonreference parameter:  T  is  int  g(7);  // reference parameter:  T  is  int =>  ERROR: can't pass  7  to  int& 

For a call f(x) , the array type of x decays to type double* , which is the type deduced for T . In f(seven) the const qualification is stripped and hence T is deduced to be int . In contrast, calling g(x) deduces T to be type double[20] (no decay occurs). Similarly, g(seven) has an lvalue argument of type int const , and because const and volatile qualifiers are not dropped when matching reference parameters, T is deduced to be int const . However, note that g(7) would deduce T to be int (because nonclass rvalue expressions never have const or volatile qualified types), and the call would fail because an argument 7 cannot be passed to a parameter of type int& .

The fact that no decay occurs for arguments bound to reference parameters can be surprising when the arguments are string literals. Reconsider our max() template:

 template<typename T>  T const& max(T const& a, T const& b); 

It would be reasonable to expect that for the expression max("Apple", "Pear") T is deduced to be char const* . However, the type of "Apple" is char const[6] , and the type of "Pear" is char const[5] . No array-to-pointer decay occurs (because the deduction involves reference parameters), and therefore T would have to be both char[6] and char[5] for deduction to succeed. That is of course impossible . See Section 5.6 on page 57 for additional discussion on this topic.

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