从函数实参来确定模板实参的过程称为模板实参推断

类型转换与模板类型参数
调用时传递给函数模板的实参被用来初始化函数形参。若一个函数形参使用了模板类型参数,就会采用特殊的初始化规则。只有很少的几种类型转换会自动应用于这些实参,编译器通常不进行类型转换而是生成新的实例
顶层const会被忽略
可以将非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,const被忽略
fref(s1, s2);//s1被转化成const
int a[10], b[42];
fobj(a, b);//数组转化成指针
fref(a, b);//形参是引用类型,无法转换,错误

非模板类型的参数可以进行正常的类型转换

函数模板显式形参

template<typename A, typename B, typename C>
A sum(B, C);//无法推断A是什么类型,需要使用显式指出
sum<long long>(i, lng);//使用尖括号指出A为long long,B和C从i和lng推断

尖括号里面从左向右指出所有typename,可以从右向左忽略,按照实参来推断

尾置返回类型与类型转换
尾置返回出现在参数列表后,所以它可以使用函数的参数

template<typename It>
auto fcn(It beg, It end) -> decltype(*beg)
{
    return *beg;
}

函数指针和实参推断
使用函数模板初始化函数指针或为函数指针赋值,编译器使用指针类型推断模板实参
当函数参数为普通左值引用时,只能传递一个左值,如果是const就添加const
当函数参数为const的引用,可以传递任何对象,传递的是否是const不影响
当函数参数为右值引用时,可以传递右值,也可以传递左值;传递左值时,编译器会推断模板类型为某类型的引用,会触发引用折叠(当间接创建了引用的引用时,如类型别名或者模板参数),成为一个左值引用。在11标准中,X&& &&将折叠为右值引用
谨慎使用右值引用,最好用重载的方式,可能产生歧义

std::move
move本质上可以接受任何类型的实参
给move赋值右值,会什么也不做,赋值左值,发生引用折叠,从而返回右值,利用了remove_reference

允许从一个左值static_cast到一个右值引用
不能隐式的将左值转换为右值引用,可以用static_cast显式的将左值转换为右值引用
但是最好还是使用move

转发
某些函数需要将其一个或多个实参连同类型不变的转发给其他函数
通过将一个函数参数定义为一个指向模板类型参数的右值引用,使用引用类型是因为引用可以保持const属性(引用类型中的const是底层的),通过引用折叠可以保持翻转实参的左值/右值属性

template<typename F, typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2)
{
    f(t2, t1);//左值可以保持,但是右值还是会有问题
}

通过调用forward来保持类型,用法跟move相似

template<typename F, typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2)
{
    f(std::forward<T2>(t2), std::forward<T1>(t1));//左值可以保持,但是右值还是会有问题
}