c++模板学习笔记(之二)
——关于函数模板参数的问题
/*-----------------------------------------------------------------------------------
欢迎评论和转载,转载请注明出处:http://blog.csdn.net/lingyin55
------------------------------------------------------------------------------------*/
如果没有特别说明,以下程序的运行环境都为vs2008。
对于function template来说,有两种参数:
一、template parameters(模板参数),如下:
template <typename T>
二、call parameters(调用参数),在function
template后说明,如下:
/*前面可能有返回类型*/
max (T const& a, T const& b);
下面先给出一个简单的例子,然后再一步一步来讨论可能出现的问题及解决办法,如下:
template <
typename T1,
typename T2 >
inline T1 max(
const T1& a,
const T2& b )
{
return a < b ? b : a;
}
int main()
{
max( 4, 5 );
return 0;
}
max的作用不用多言,那么上面的程序有什么特别需要注意的呢?
一、注意到类型T1跟T2被定义成不同类型(当然,如果传入类型相同也可),这样有个好处,对于不同类型的值可以调用该函数模板,如max(4,4.1);一个整型和一个浮点型。这里有个地方要说明,并不是对于任意的类型都可以传入调用,原因很简单,传入的类型如果不同,必须满足其中一种类型通过隐式转换可以转成另外一种类型,否则出错。如max(4,”hello”);//出错
二、function template不能有默认模板参数,如下则报错
int c = 10;//定义一个变量,用于作为T2的默认值
//为b设定一个默认参数
template <
typename T1,
typename T2 >
inline T1 max(
const T1& a,
const T2& b = c )
{
return a < b ? b : a;
}
int main()
{
//调用时出错,提示无法为T2进行类型推导
max( 4 );
return 0;
}
其中第二个参数用了默认参数,结果编译器提示错误“could not deduce template
argument for 'T2'”。这点也class template不同,在class
template中允许传入默认参数(class template后续讨论)。
三、对于上面的程序,不能以引用的(by
reference)的方式返回值。如把程序改为
template <
typename T1,
typename T2 >
inline
const T1& max(
const T1& a,
const T2& b )
{
return
a < b ? b : a;
}
int main()
{
std::cout << max( 4, 5.2 ) << std::endl;
return 0;
}
输出是5,但程序是有问题的,因为返回值类型是int,对于5.2(编译器认为是double型),编译器会强制转换为int的5(此时编译器会有警告,提示从double转为int会导致精度的丢失),并用一个int的临时变量来保存返回的结果,最后将这个临时变量返回。我们知道,当函数的作用域结束时,临时变量会被释放掉,因此返回临时变量的引用或者地址(其实道理一样),都是不明智的,因此上面的程序只能返回T1,而不能返回T1&。
当然,如果类型相同,如max( 4, 5)都为整型,则不会出现上面的问题,但此时何不把T1和T2定义为相同的类型呢?如
inline
const T& max(
const T& a,
const T& b )。实际上,为了保持代码的简单,推荐的做法便是尽量将函数模板的类型定义为相同的,即【单一类型】。
四、或许已经有人想到,其实可以再定义一个类型作为函数模板的返回类型,如下的RT
template<typename
T1, typename T2,
typename RT>
inline
const RT& max(
const T1& a,
const T2& b );
但是请先明白,函数模板对于参数的推导,仅仅限于传入的调用参数,对于返回值的类型,推导机制并不会对其与返回值进行匹配,因此编译器无法推导出RT。
怎么办呢?既然编译器无法自己推导,那我们可以明确指定啊!因此对于上面的函数模板,我们可以使用这种方法调用max<int,double,double>(4,
5.2);<>中的第三个类型对应于返回类型RT。
为了少写一点参数,其实可以对模板中声明的类型顺序改变一下,因为函数模板的调用参数(call
parameters)还是可以利用编译器自己去推导的,改后的声明如下
template<typename
RT, typename T1,
typename T2>
inline
const RT&
max( const
T1& a, const
T2& b );
调用方式max<double>(4,
5.2);//只需声明函数的返回类型是double