In which cases one needs to specify the template's argument `types` specifically?
// Function declaration.
template <typename T1,
typename T2,
typename RT> RT max (T1 a, T2 b);
// Function call.
max &开发者_JAVA百科lt;int,double,double> (4,4.2)
// Function call.
max <int> (4,4.2)
One case may be when you need to specify the return type.
Is there any other situation which requires the argument types to be specified manually?
(1) When there is no argument to the function and still it's a template
type, then you may have to specify the arguments explicitly
template<typename T>
void foo ()
{}
Usage:
foo<int>();
foo<A>();
(2) You want to distinguish between value and reference.
template<typename T>
void foo(T obj)
{}
Usage:
int i = 2;
foo(i); // pass by value
foo<int&>(i); // pass by reference
(3) Need another type to be deduced instead of the natural type.
template<typename T>
void foo(T& obj)
{}
Usage:
foo<double>(d); // otherwise it would have been foo<int>
foo<Base&>(o); // otherwise it would have been foo<Derived&>
(4) Two different argument types are provided for a single template parameter
template<typename T>
void foo(T obj1, T obj2)
{}
Usage:
foo<double>(d,i); // Deduction finds both double and int for T
If the function template parameter appears in the function parameter list, then you don't need to specify the template parameters. For example,
template<typename T>
void f(const T &t) {}
Here T
is a template parameter, and it appears in the function parameter list, i.e const T &t
. So you don't need to specify the template parameter when calling this function:
f(10); //ok
Since the type of 10
is int
, so the compiler can deduce the template parameter T from it, and T
becomes int
.
Note that since the type deduction is done from using the information of the function arguments, its called template argument deduction. Now read on.
If the template parameter doesn't appear in the function parameter list, then you have to provide the template parameter. Example:
template<typename T>
void g(const int &i) {}
Notice g()
is different from f()
. Now T
doesn't appear in the function parameter list. So:
g(10); //error
g<double>(10); //ok
Note that if a function template templatizes on the return type as well, and the return type is different from the types appearing the function parameter list, then you've to provide the return type:
template<typename T>
T h(const T &t) {}
Since return type T
is same as the function parameter, type deduction is possible from function argument:
h(10); //ok - too obvious now
But if you've this:
template<typename R, typename T>
R m(const T &t) {}
Then,
m(10); //error - only T can be deduced, not R
m<int>(10); //ok
Note that even though the function template m
has templatized on two types : R
and T
, we've provided only ONE type when calling it. That is, we've written m<int>(10)
as opposed to m<int,int>(10)
. There is no harm in writing the later, but its okay, if you don't. But sometimes you've to specif both, even if one type T
can be deduced. It is when the order of type parameters is different as shown below:
template<typename T, typename R> //note the order : its swapped now!
R n(const T &t) {}
Now, you've to provide both types:
n(10); //error - R cannot be deduced!
n<int>(10); //error - R still cannot be deduced, since its the second argument!
n<int,int>(10); //ok
The new thing here is : the order of type parameters is also important.
Anyway, this covers only the elementary concept. Now I would suggest you to read some good book on templates, to learn all the advanced things regarding type deduction.
In general, you need to explicitly specify the types when the compiler can't figure it out on its own. As you mentioned, this often happens when the return type is templatized, since the return type cannot be inferred from the function call.
Template classes have the same problem -- instantiating a std::vector
offers no way for the compiler to determine what type your vector is storing, so you need to specify std::vector<int>
and so forth.
The type resolution is only performed in the case of function arguments, so it may be easier to view that as the special case; ordinarily, the compiler is unable to guess what type(s) to use.
The simple answer is that you need to provide the types when the compiler cannot deduce the types by itself, or when you want the template to be instantiated with a particular type that is different from what the compiler will deduce.
There are different circumstances when the compiler cannot deduce a type. Because type deduction is only applied to the arguments (as is the case with overload resolution) if the return type does not appear as an argument that is deducible, then you will have to specify it. But there are other circumstances when type deduction will not work:
template <typename R> R f(); // Return type is never deduced by itself
template <typename T>
T min( T const & lhs, T const & rhs );
min( 1, 2 ); // Return type is deducible from arguments
min( 1.0, 2 ); // T is not deducible (no perfect match)
min<double>( 1.0, 2 ); // Now it is ok: forced to be double
min<double>( 1, 2 ); // Compiler will deduce int, but we want double
template <typename T>
void print_ptr( T* p );
print_ptr<void>( 0 ); // 0 is a valid T* for any T, select manually one
template <typename T>
T min( T lhs, T rhs );
int a = 5, b = 7;
min<int&>(a,b)++; // Type deduction will drop & by default and call
// min<int>(a,b), force the type to be a reference
template <typename C>
typename C::value_type
min_value( typename C::const_iterator begin, typename C::const_iterator end );
std::vector<int> v;
min_value<std::vector<int> >( v.begin(), v.end() );
// Argument type is not deducible, there are
// potentially infinite C that match the constraints
// and the compiler would be forced to instantiate
// all
There are probably more reasons for an argument type cannot be deduced, you can take a look at §14.8.2.1 in the standard for the specifics of deduction of arguments from a function call.
精彩评论