C++17 类模板参数推导

C++17 类模板参数推导
枫C++17 类模板参数推导
C++17 引入了
类模板参数推导(Class Template Argument Deduction,简称 CTAD)这一特性。它的作用很直接:当我们构造一个类模板对象时,编译器可以根据构造函数的参数自动推导模板参数类型,这样很多场景下就不用再手写<T>了。
1. 为什么会有类模板参数推导?
在 C++17 之前,函数模板可以自动推导类型,但类模板一般不行。比如下面这个简单的 Pair:
1 | template <typename T1, typename T2> |
在旧写法中,必须显式写出模板参数:
1 | Pair<int, double> p(1, 3.14); |
虽然这段代码没有问题,但很多时候模板参数其实已经完全体现在构造函数参数里了,再写一遍就显得有些重复。
有了 CTAD 以后,可以直接写成:
1 | Pair p(1, 3.14); |
编译器会自动推导出这是一个 Pair<int, double>。
2. 基本使用
2.1 自定义类模板
还是以上面的 Pair 为例:
1 |
|
输出:
1 | 10, 3.14 |
这里 p 的真实类型就是 Pair<int, double>。
2.2 标准库中的使用
CTAD 在标准库里也很常见,例如:
1 |
|
这种写法在代码量比较多的时候会显得更简洁,尤其是模板参数本身比较长的时候,效果更明显。
3. 推导指南
有些场景下,编译器虽然能看到构造参数,但未必能按我们的预期推导出模板参数,这时就需要推导指南(Deduction Guide)。
3.1 一个简单例子
1 |
|
如果没有这一行:
1 | Box(const char*) -> Box<std::string>; |
那么 Box b("hello"); 推导出来的类型更可能是 Box<const char*>。
加入推导指南之后,就可以明确告诉编译器:当构造参数是 const char* 时,我希望它推导成 Box<std::string>。
3.2 多参数场景
1 | template <typename T1, typename T2> |
这条推导指南表示:如果两个参数类型相同,那么就把它们都推导成同一种类型。
虽然这个例子中编译器大多也能推导出来,但推导指南在更复杂的构造函数设计里会很有用。
4. 使用时要注意的几点
4.1 不是所有类模板都能自动推导
只有在构造函数信息足够明确时,编译器才有办法推导模板参数。
如果类没有合适的构造函数,或者构造函数里看不出模板参数类型,就没法自动推导。
4.2 花括号初始化要注意元素类型
1 | std::vector v1{1, 2, 3}; // std::vector<int> |
这种写法很自然,但如果花括号里的元素类型混杂,推导结果可能不是你预期的,甚至会直接编译失败。
4.3 推导出来的类型不一定是“最方便”的类型
比如字符串字面量默认是 const char*,如果你想要的是 std::string,通常就需要自己显式写类型,或者像上面那样提供推导指南。
4.4 可读性仍然要放在前面
虽然 CTAD 很方便,但并不是所有地方都适合省略模板参数。
如果省略之后反而让代码不直观,那还不如老老实实写完整。
5. 一个更贴近日常开发的例子
1 |
|
这里 std::pair item(1, string("apple")); 就使用了类模板参数推导。写法上会比 std::pair<int, string> 更轻一点。
总结
C++17 的类模板参数推导本质上是帮我们减少模板参数的重复书写。对于标准库容器、自定义简单模板类,以及一些只看构造参数就能确定类型的场景,这个特性都很实用。
不过在使用时也要注意两点:一是并不是所有模板类都能顺利推导,二是不要为了“省几个字”牺牲代码可读性。写得自然、看得明白,才是最重要的。














