Flylib.com

Books Software

 
 
 

11.2 Deduced Contexts

Ru-Brd

11.2 Deduced Contexts

Parameterized types that are considerably more complex than T can be matched to a given argument

type. Here are a few examples that are still fairly basic:

template<typename T> 
void f1(T*); 

template<typename E, int N> 
void f2(E(&)[N]); 

template<typename T1, typename T2, typename T3> 
void f3(T1 (T2::*)(T3*)); 

class S { 
  public: 
    void f(double*); 
}; 

void g (int*** ppp) 
{ 
    bool b[42]; 
    f1(ppp);

// deduces

T

to be

int** 
    f2(b);

// deduces

E

to be

bool

and

N

to be

42 
    f3(&S::f);

// deduces

T1 = void

,

T2=S

, and

T3 = double 
}

Complex type declarations are built from more elementary constructs (pointer, reference, array, and function declarators; pointer-to-member declarators; template-ids; and so forth), and the matching process proceeds from the top-level construct and recurses through the composing elements. It is fair to say that most type declaration constructs can be matched in this way, and these are called deduced contexts . However, a few constructs are not deduced contexts:

  • Qualified type names . A type name like Q<T>::X will never be used to deduce a template parameter T , for example.

  • Nontype expressions that are not just a nontype parameter. A type name like S<I+1> will never be used to deduce I , for example. Neither will T be deduced by matching against a parameter of type int(&)[sizeof(S<T>)] .

These limitations should come as no surprise because the deduction would, in general, not be unique (or even finite), although qualified type names are sometimes easily overlooked. A nondeduced context does not automatically imply that the program is in error or even that the parameter being analyzed cannot participate in type deduction . To illustrate this, consider the following, more intricate example:


// details/fppm.cpp

template <int N> 
class X { 
  public: 
    typedef int I; 
    void f(int) { 
    } 
}; 

template<int N> 
void fppm(void (X<N>::*p)(X<N>::I)); 

int main() 
{ 
    fppm(&X<33>::f);

// fine:

N

deduced to be

33 
}

In the function template fppm() , the subconstruct X<N>::I is a nondeduced context. However, the member-class component X<N> of the pointer-to-member type is a deducible context, and when the parameter N , which is deduced from it, is plugged in the nondeduced context, a type compatible with that of the actual argument &X<33>::f is obtained. The deduction therefore succeeds on that argument-parameter pair.

Conversely, it is possible to deduce contradictions for a parameter type entirely built from deduced contexts. For example, assuming suitably declared class templates X and Y :

template<typename T> 
void f(X<Y<T>, Y<T> >); 

void g() 
{ 
    f(X<Y<int>, Y<int> >());

// OK

f(X<Y<int>, Y<char> >());

// ERROR: deduction fails

}

The problem with the second call to the function template f() is that the two arguments deduce different arguments for the parameter T , which is not valid. (In both cases, the function call argument is a temporary object obtained by calling the default constructor of the class template X .)

Ru-Brd
Ru-Brd

11.3 Special Deduction Situations

There are two situations in which the pair ( A , P ) used for deduction is not obtained from the arguments to a function call and the parameters of a function template. The first situation occurs when the address of a function template is taken. In this case, P is the parameterized type of the function template declarator, and A is the function type underlying the pointer that is initialized or assigned to. For example:

template<typename T> 
void f(T, T); 

void (*pf)(char, char) = &f;

In this example, P is void(T, T) and A is void(char, char) . Deduction succeeds with T substituted with char , and pf is initialized to the address of the specialization f<char> .

The other special situation occurs with conversion operator templates. For example:

class S { 
  public: 
    template<typename T, int N> operator T[N]&(); 
};

In this case, the pair ( P , A ) is obtained as if it involved an argument of the type to which we are attempting to convert and a parameter type that is the return type of the conversion operator. The following code illustrates one variation:

void f(int (&)[20]); 

void g(S s) 
{ 
    f(s); 
}

Here we are attempting to convert S to int (&)[20] . Type A is therefore int[20] and type P is T[N] . The deduction succeeds with T substituted with int and N with 20 .

Ru-Brd