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
.)
|