Given a template function, that does not use the template parameter for input directly. How would C++ type inference work? For instance given
template<typename T>
void f(T::value_type){开发者_开发知识库}
when (if at all) will type inference work for this function?
Is there any other place except of template<typename T1,...>void f(T1,T2,...)
where type inference might occur?
As always, quote the standard for extra credit.
It never works for a nested type, unless T is also a parameter.
If you call f(1)
, the compiler has no chance of finding all T's with a nested typedef int value_type;
.
You can infer a type that is part of the parameter type, like
template<class T>
void f(std::vector<T>);
I think here's your answer:
14.8.2.4 - Deducing template arguments from a type [temp.deduct.type]
[...]
-3- [...]
In most cases, the types, templates, and non-type values that are used to compose
P
participate in template argument deduction. That is, they may be used to determine the value of a template argument, and the value so determined must be consistent with the values determined elsewhere. In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in nondeduced contexts and is not explicitly specified, template argument deduction fails.-4- The nondeduced contexts are:
The nested-name-specifier of a type that was specified using a qualified-id.
A type that is a template-id in which one or more of the template-arguments is an expression that references a template-parameter.
When a type name is specified in a way that includes a nondeduced context, all of the types that comprise that type name are also nondeduced. However, a compound type can include both deduced and nondeduced types. [Example: If a type is specified as
A<T>::B<T2>
, bothT
andT2
are nondeduced. Likewise, if a type is specified asA<I+J>::X<T>
,I
,J
, andT
are nondeduced. If a type is specified asvoid f(A<T>::B, A<T>)
, theT
inA<T>::B
is nondeduced but theT
inA<T>
is deduced. ]
Your T::value_type
is a qualified-id of a type, so types in its nested-name-specifier are nondeduced and must be specified explicitly.
Edit: this information is from ISO/IEC 14882:1998.
The standard requires you to disambiguate the dependent type:
template<typename T>
void f(typename T::value_type){}
In the past there have been some less-standard behaviours in this area, resulting in code that compiled on one (MSVC) but not on the other (GCC) compiler. These days, likely under the influence of heavily generic standard libraries like Boost, compilers seem to accept the correct code only.
The typename keyword is sometimes needed to disambiguate identifiers dependent on template arguments (also this). Think of it this way: you have to give the compiler enough information to complete a syntax check the first time 'round while parsing a template definition. The actual template arguments aren't known at that time and (C++ having an involved grammar) you have to give the compiler hints what kind of symbol a token is going to represent later on
Unless you specify the T
parameter somehow, the compiler will not try to deduct T
in any way.
Even if you specify it explicitly, I believe it will work only where the original definition is provided, not though typedefs.
Consider the following example (compiled with clang++, apparently g++ fails on that):
#include <stdio.h>
template <typename T>
void foo(T) {
printf("foo(T)\n");
}
template <typename T>
void foo(typename T::value) {
printf("foo(T::value)\n");
}
struct X {
class value {};
};
struct Z {
typedef int value;
};
struct XZ {
typedef Z value;
};
typedef X::value Xv;
#define CALL(function,param) printf(#function " (" #param ") = "); function(param());
void explicitCalls() {
printf("Explicit calls:\n");
CALL(foo<int>,int);
CALL(foo<X::value>,X::value);
CALL(foo<Z::value>,Z::value);
CALL(foo<XZ::value>,XZ::value);
CALL(foo<Xv>,Xv);
}
void implicitCalls() {
printf("Implicit calls:\n");
CALL(foo,int);
CALL(foo,X::value);
CALL(foo,Z::value);
CALL(foo,XZ::value);
CALL(foo,Xv);
}
int main() {
explicitCalls();
implicitCalls();
}
The output is:
Explicit calls:
foo<int> (int) = foo(T)
foo<X::value> (X::value) = foo(T::value)
foo<Z::value> (Z::value) = foo(T)
foo<XZ::value> (XZ::value) = foo(T)
foo<Xv> (Xv) = foo(T::value)
Implicit calls:
foo (int) = foo(T)
foo (X::value) = foo(T)
foo (Z::value) = foo(T)
foo (XZ::value) = foo(T)
foo (Xv) = foo(T)
精彩评论