1.模板实参推断
1.1 自动类型转换
const转换:非const对象的引用(指针)传递给一个const的引用(指针)形参。
数组或函数指针转换:若函数实参不是引用类型,则可以对数组或函数类型的实参应用正常的指针转换。
1.2 函数模板显示实参
template <typename T1,typename T2,typename T3>
T1 sum(T2,T3); //T2,T3可以通过调用实参推断,T1无法推断,需要显示指定
sum<long long>(23,23.5); //显示指定T1的类型
1.3 尾置返回类型
原:
当我们不知道返回结果的准确类型,但知道是所处理序列的元素类型。
解:
template <typename It>
auto fcn1(It beg,It end)->decltype(*beg)
{
return *beg; //返回引用
}
template <typename It>
auto fcn2(It beg,It end)->typename remove_reference<decltype(*beg)>::type
{
return *beg; //返回序列中一个元素的拷贝
}
1.4 函数指针和实参推断(使指针指向一个模板实例)
template <typename T>
int compare(const T&,const T&){/*...*/}
int (*pf)(const int&,const int&) = compare; //指向compare的int实例版本
当参数是一个函数模板实例的地址时,程序上下文必须满足:对每个模板参数,能唯一确定其类型或值。
void fcn(int(*)(const string&,const string&));
void fcn(int(*)(const int&,const int&));
fcn(compare); //歧义
fcn(compare<int>); //显示指定
2.模板实参推断和引用
2.1 模板实参推断:
/*函数参数为左值引用*/
template <typename T> void f1(T&); //可以传入i(int),ci(const int),右值不可以
template <typename T> void f2(const T&); //三者均可,T都为int
/*函数参数为右值引用*/
template <typename T> void f3(T&&); //传入i(int&),ci(const int&),右值(int)
2.2 引用折叠
T& &|T& &&|T&& &————>T&
T&& && ---->T&&
若函数实参是指向模板参数的右值引用(T&&),则可传递给它任意类型的实参。
左值=》T&:函数参数通过引用折叠被实例化为(int& &&=》int&)
/*std::move*/
template <typename T>
typename remove_reference<T>::type&& move(T&&t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
}
2.3转发
参数通过一个函数转发给另一函数发生调用。
template <typename F,typename T1,typename T2>
void flip1(F f,T1 t1,T2 t2)
{
f(t1,t2); //调用另一个函数
}
void f(int &v1,int v2){/*改变v1*/}
问题:
当调用flip1(f , j , 42)时,j的值并没有被改变,改变的只是临时的t1。
改进版:
template <typename F,typename T1,typename T2>
void flip2(F f,T1 &&t1,T2 &&t2)
{
f(t1,t2); //调用另一个函数
}
void g(int &v1,int &&v2){/*...*/}
此时调用flip1(f , j , 42),j的值会发生改变,但t2变成了左值(函数参数变量均为左值),无法传给右值引用。
最终版:
template <typename F,typename T1,typename T2>
void flip3(F f,T1 &&t1,T2 &&t2)
{
f(std::forward<T1>(t1),std::forward<T2>(t2)); //调用另一个函数
}
j –> (int&) t1 –> T1(int&) –> std::forward< T1 > –> int&
42 –> t2(int) –> T2(int) –> std::forward< T2 > –> int&&
2.4 重载与模板
- 当多个重载模板对一个调用提供同样好的匹配时,应选择最特例化的版本。
- 对于一个调用,如果一个非函数模板与一个函数模板提供同样好的匹配,则选择非模板版本。
2.5 可变参数模板
参数包:可变数目的参数(≥0),用”…“表示一个模板参数为一个包。
sizeof…(参数名):返回包中有多少元素。
template<typename T,typename...Args> //Args为模板参数包
void fcn(const T&,const Args&...rest); //args为函数参数包
sizeof...(Args)==sizeof(rest);
fcn(str,42,"hi")==>fcn(const string&,const int&,const char[3]&);
包扩展
- rest…:生成实参列表
- fcn(rest)…:对rest包中的每个元素进行进行fcn调用(fcn(r1),fcn(r2)…)
- std::forward< Args >(args)…:扩展为std::forward< T1 >(t1),std::forward< T2 >(t2)…
/*make_share<T>(...)*/
template <typename T,typename...Args>
shared_ptr<T> make_share(Args&&...args)
{
return shared_ptr<T>(new T(std::forward<Args>(args)...));
}
2.6模板特例化
通用模板的定义对特定类型不合适,如无法对指针进行比较,本质是实例化一个模板。
/*函数模板特例化*/
template <> //<>表明我们将为所有模板参数提供实参
int compare(const char* const&p1,const char* const&p2)
{/*...*/}
/*类模板特例化*/
template <>
class A<int> //显示指定特例化类型
{
/*....*/
};
类模板部分特例化
template<class T>
struct remove_reference{typedef T type;} //普通
template<class T>
struct remove_reference<T&>{typedef T type;} //左值
template<class T>
struct remove_reference<T&&>{typedef T type;} //右值
remove_reference<decltype(42)>::type //int-->int
remove_reference<decltype(i)>::type //int&-->int
remove_reference<decltype(std::move(i))>::type //int&&-->int
特例化成员
template <typename T>
struct Foo
{
void bar(){/*...*/}
};
template<>
void Foo<int>::bar(){/*...*/} //当T为int时,调用特例化的bar