前文回顾:
C++ 泛型编程(一) 基本概念
C++ 泛型编程(二) 函数模版
模版实参推断
-
类型转换
编译器通常不是对实参进行类型转换,而是生成一个新的模版实例,只有有限的几种类型会进行类型转换。可以将一个非 const 对象的引用或者指针传递给一个 const 引用或者指针的形参;若形参不是引用类型,可以对数组或者函数类型进行正常的指针转换。
template <typename T> T fobj(T,T); template <typename T> T fref(const T&,const T&); string s1("a value"); const string s2("another value"); fobj(s1,s2);//实例化为 fobj(string,string),const 被忽略 fref(s1,s2);//实例化为 fref(const string&,const string&) int a[10],b[40]; fobj(a,b);//实例化为 fobj(int*,int*) fref(a,b);//错误,两个数组类型不匹配,形参是引用,数组不会转换为指针
如果函数参数类型不是模版参数,则可以对实参进行正常的类型转换。
-
显式实参
如果返回值参数作为了模板类型,则编译器无法推导出返回值类型,必须需要显示指定返回值模板类型。一般将返回值参数作为第一个模版类型。
template <typename T1,typename T2,typename T3> T1 sum(T2,T3); //返回值类型必须指定,其他参数可用推导出来 sum<long>(5,10);// 实例化为 long sum(int,int)
若对模版类型参数已经显示指定了函数实参,则可用进行正常的类型转换。
long i = 10; compare<int>(i,2);// 实例化为 compare(int,int),i 自动转换为 int
-
尾置返回类型
当无法提前确定返回值类型时,为了定义此函数则必须使用尾置返回类型。
template <typename It> auto fcn(It beg,It end) -> decltype(*beg) { return *beg; } vector<int> vi = {1,2,3,4}; auto &i = fcn(vi.begin(),vi.end());// 迭代器返回的是元素的引用,推断出返回值类型为 int&
-
函数指针
当我们用一个函数模版初始化一个函数指针或者为一个函数指针赋值时,编译器使用指针的类型来推断模版实参。
template <typename T> int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; } int (*Fint)(const int &, const int &);//函数指针 Fint = compare;//指针赋值,实例化为 conpare(int,int)
-
引用实参推断
①.非 const 左值引用
模版类型参数是一个普通左值引用时:T &,实参只能传递一个左值;若实参是 const ,则 T 推断为 const。
template <typename T> void f1(T&);//实参必须是左值 int i = 2; const j = 3; f1(i);// T 推断为 int f1(j);// T 推断为 cont int f1(5);//错误,不能传递右值
②.const 左值引用
模版类型参数是一个 const 左值引用时:const T &,实参可以传递任何类型;此时 const 不再是 T 的一部分。
template <typename T> void f2(const T&);//实参可以是任何类型 int i = 2; const j = 3; f2(i);//可以传递左值, T 推断为 int f2(j);// T 推断为 int f2(5);//可以传递右值,T 推断为 int
③.右值引用
模版类型参数是一个右值引用时:T &&,实参可以传递一个右值;右值引用一般为非 const ,const 右值引用未发现有实际用途。
template <typename T> void f3( T&&);//实参可以是任何类型 f3(23);// 可以传递右值,T 推断为 int
④.引用折叠
将一个左值传递给右值引用参数时,编译器会推断参数类型为实参的左值引用类型;间接的创建了引用的引用时,会触发引用折叠。
// X& &、X&& &、X& && 都折叠成类型 X& // 只有X&& && 都折叠成类型 X&&
如果一个函数参数是指向模版参数类型的右值引用,则可以传递给它任何类型的实参。如果传递一个左值,则函数参数实例化成一个普通左值引用。
template <typename T> void f3( T&&);//实参可以是任何类型 f3(23);// 可以传递右值,T 推断为 int int i = 2; f3(i);// 实参是左值,T 推断为 int&,实例化为:void f3<int&>(int &)
-
完美转发
完美转发是指在函数模板中完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数,并且保存实参的所有性质不变,包含 const 以及左值还是右值;可以使用 std::forward(T) 及引用折叠的规则,实现完美转发。