先序文章请看C++模板元编程详细教程(之一)
模板参数自动推导
在上一章我们讲解了C++模板的基本用法,并且强调了模板并非直接可用的代码,需要经历「实例化」,而实例化的过程其实就是指定参数的过程。
但如果模板只能通过显式指定参数来进行实例化的话,那C++模板的「威力」也就止步于此了,进而也就不会出现复杂的模板元编程体系这样的东西。所以C++模板还有一个非常重要的特性,就是模板参数的自动推导。
模板参数的自动推导主要分为3种:
- 根据函数参数自动推导(模板函数)
- 根据构造参数自动推导(模板类)
- 自定义构造推导(模板类)
需要强调的一点是,自动推导只能推导类型参数,而整数和整数派生参数是没法推导的,只能靠显式传入。
下面就来逐一介绍。
根据函数参数自动推导
本条针对的是模板函数。再次强调,模板参数和函数参数是不同的东西,用于实例化模板的参数是模板参数,用于调用函数的参数是函数参数。直观上来说,尖括号里的是模板参数,圆括号里的是函数参数。
我们先来看一个例子:
template <typename T>
void show(T t) {
std::cout << t << std::endl;
}
void Demo() {
int a = 5;
show(a); // [1]
show(5); // [2]
}
在上述例程中,show是一个模板函数,在下面两处标记的位置我们是直接调用show的,而没有指定模板参数,那么这时,就会触发模板参数的自动推导。
先来看一下[1]位置的调用,由于我们传入了参数a,编译器就会根据a的类型来推导模板参数T。在模板函数show的声明处,参数列表是(T t),所以T会根据a的类型来推导。那么问题来了,这里到底会推导出int还是int &还是const int还是const int &?
答案也很简单,模板参数的自动推导是完全按照auto的推导原则进行的。(如果读者对这部分还不是很清楚的话可以参考《C++的缺陷和思考(三)》中「auto推导策略」章节的内容。)
也就是说,这里相当于auto t = a;,会推导出int,因此T会推导出int。
同理,针对于[2]位置的调用,我们传入的是一个常量5,照理说int、const int、const int &、int &&、const int &&都可以匹配,但根据auto的推导原则,仅仅保留「最简类型」,所以仍然会推导出int。
那么[1]和[2]的位置其实相当于:
show<int>(a); // [1]
show<int>(5); // [2]
再次强调,推导的原则与auto相同。那么同样地,也就支持和*、&、&&、const的组合,下面给出几个实例:
template <typename T>
void f1(T &t) {
}
template <typename T>
void f2(const T &t) {
}
template <typename T>
void f3(T *p) {
}
void Demo() {
f1(5); // 会报错,因为会推导出f1<int>,从而t的类型是int &,不能绑定常量
int a = 1;
f1(a); // f1<int>,t的类型是int &
f2(a); // f2<int>,t的类型是const int &
f3(a); // 会报错,因为会推导出f3<int>,此时t的类型是int *,int不能隐式转换为int *
f3(&a); // f3<int>, t的类型是int *
}
这里需要注意的是,T是按照auto法则来推导的,但由于我们加上了修饰符,所以实际的函数参数t的类型是会带上这种描述符的,详情可以看上面例程的注释。
既然模板参数类型推导是按照auto法则,那就不得不提到一个特殊的推导,它就是auto &&。我们知道auto &&会根据绑定对象的左右性来推导出左值引用或是右值引用。(如果读者对这部分不清楚的话,可以参考《C++的缺陷和思考(二)》中「引用折叠」章节的内容。)同理对于用&&修饰的参数,在自动推导时也会拥有这样的特性。
换句话说,T &&也可以绑定可变值,此时会推导出左值引用。请看下面例程:
template <typename T>
void f4(T &&t) {
}
void Demo(

本文详细介绍了C++模板的自动类型推导机制,包括根据函数参数和构造参数自动推导模板参数类型的方法,以及如何使用推导指南来自定义构造推导。
最低0.47元/天 解锁文章
1716





