Cpp 模板:模板实参推断
模板类型转换
函数模板的实参可以被用来初始化函数的形参,并且如果这个函数的形参类型使用了模板类型参数,根据特殊的初始化规则,同样会初始化模板类型参数
只有两种转换可以:
- const 转换:非 const 对象的引用或指针传递给一个 const 的引用或指针形参
- 数组或函数指针的转换:如果形参不是引用类型,会对数组或函数类型的实参进行指针转换,前者为指向首元素的指针,后者为对应函数类型的指针
其他转换:
- 算术转换
- 派生类到基类的转换
- 用户定义的转换
都不能应用于函数模板
1 | template <typename T> T fobj(T, T) {} |
显式指定实参的类型转换
如果模板类型参数已经显式指定,也可以像普通函数一样进行类型转换
1 | template<typename T> |
像是使用父类实例化,传入形参是子类同样可以
利用显式指定实参的类型转换,传入两个不同的类型
使用方式,标准库的 max
,接收两个参数返回较大的实参
1 | int i = 0, j = 1; |
有的模板要求显式传入模板实参
例如:std::make_shared
,就要求显式传入实参,因为需要根据传入的模板实参计算需要分配多大的空间
尾置返回类型的类型转换
假如有如下模板:
1 | template<typename _Iterator> |
我们希望让这个函数接收容器的一对迭代器,并返回序列中一个元素的引用
但是这里我们只设置了一个接收迭代器了类型的模板参数,而不知道容器内部元素的具体类型
在调用点的语法为:
1 | vector<int> vi{0}; |
decltype(*beg)
可以获取到元素对类型,但是在函数调用点auto &ele = fcn(vi.begin(), vi.end());
所能提供给编译器的信息,只有传入的迭代器类型,在真正开运行进入迭代之前,我们都无法得到这个*beg
,自然也就无法在编译阶段让编译器得知这个元素的类型
这时就需要用到尾置返回类型
1 | template<typename _Iterator> |
对迭代器解引用后得到一个左值,再经过decltype
后推断出是一个元素类型的引用,最后经过auto
,会将引用去掉,从而得到元素的类型
这里的 fcn_ret
实际上是一个 int
类型
不要返回引用,要返回值
std::remove_reference
使用方式:
1 | std::remove_reference<int&> // 得到 int |
函数指针和实参推断
用一个函数模板初始化一个函数指针或给一个函数指针赋值,模板将从指针的类型进行实参推断
1 | template<typename T> int compare(const T&, const T&); |
如上边代码所示,compare
模板函数会根据pf1
的指针类型,来推断实参
返回值和形参列表都可以正常推断:
1 | template<typename T, typename U, typename M> U compare(const T&, const M&) { return U(); } |
但是对于重载函数,会出现歧义,无法确定func
的实参的唯一实例化版本,导致编译失败
1 | void func(int(*)(const int&, const int&)); |
但可以显示指出需要实例化的版本
1 | func(compare<int>); |
题外话:这里我把func(compare<int>)
放在全局,会报错,显示没有存储类或类型说明符,必须要放到一个函数内(没搞明白为什么)
1 | void Test() |