c++模板类型推断分为类模板类型推断和函数模板类型推断,这里主要讨论的是c++函数模板推断.
函数模板类型推断情况分为三种:
情况1:
template<typename T>
void func(T t)
{
//....
}
此种情况最为简单,对于传递给函数模板的实参val,假设其类型为ParaType,若ParaType具有引用属性,则去掉引用属性,若具有const属性也去掉,最终得到的类型即为T的类型:
int i = 999;
func(i); //实参i既无引用属性,也无const属性,T类型为int
const int ci = 250;
func(ci); //实参ci有const属性,去掉,T类型为int
const int &cref = 520;
func(cref); //去掉实参cref引用属性和const属性,T类型为int
情况2:
template<typename T>
void func(T &t)
{
//...
}
这种情况下,对于传递给函数模板的实参val,类型为ParaType,若ParaType具有引用属性,则去掉引用属性,若具有const属性则保留。
int i = 999;
func(i); //实参i既无引用属性,也无const属性,T类型为int
const int ci = 250;
func(ci); //实参ci有const属性,T类型为const int
const int &cref = 520;
func(cref); //去掉实参cref引用属性,T类型为const int
const int &&crref = 6666;
func(crref); //去掉实参crref引用属性,T类型为const int
情况3:
template<typename T>
void func(T &&t)
{
//...
}
这种情况即为所谓的万能引用,实参类型可以为任意类型(左值、右值、左值引用、右值引用,虽然将模板函数声明为const T&
也可以接收任意类型,但是存在不能修改实参的限制)。
对于传递给此函数模板的实参:
- 当实参类型为左值 ** (注意左值、左值引用、右值引用均为左值) ** 时,T的类型会被推断为
type&
;当实参类型为右值时,T的类型会被推断为type
。那type
什么是类型呢?type
即上述情况二中推导规则。
举例:
std::string s("fuck");
func(s); //s为左值,type为std::stirng , T被推断为std::string &
const std::string &lref = s;
func(lref); //lref为左值,type为const std::string T被推导为const std::string &
const std::string &&rref = std::string("fuck c++");
func(rref); //同上
- 当实参类型为右值时,T的类型被推断为实参相应类型。
举例:
func(std::string("hello c++"); //T被推断为std::string
std::string s("give up c++");
func(std::move(s)); //T被推断为std::string
func(static_cast<std::string &&>(s)); //T被推断为 std::string
到此为止T的类型全部已知,直接将推断类型带入到函数模板即可得到实例化函数,例如:
//情况一
func(int t) //T为int
{
//...
}
//情况二
func(const int &t) //T为const int
{
//...
}
//情况三,实参为左值时
func(const std::string& &&t) //T为const std::string &
{
//...
}
//情况三,实参为右值时
func(std::string &&t) /T为std::string
{
//...
}
在情况三中,当实参为左值时其实例化结果有点奇怪,编译器并不允许程序员直接这样写,通过引用折叠,最终版本为:
//可以看到,当实参为左值类型(左值、左值引用、右值引用)时,最终t都会绑定到一个左值对象或右值对象上
func(const std::string &t)
{
//....
}