模板参数自动推导
在C++17之前,类模板构造器的模板参数是不能像函数模板的模板参数那样被自动推导的,比如我们无法写
std::pair a{1, "a"s}; // C++17
而只能写
std::pair<int, string> a{1, "a"s}; // C++14
为了弥补这一缺陷,标准库为我们提供了 std::make_pair 函数,通过函数模板的模板参数自动推导的功能,
免去我们在构造 pair 时写模板参数的麻烦。
auto a = std::make_pair(1, "a"s); // C++14
// 相当于
// std::pair<int, string> a = std::make_pair<int, string>(1, string("a"));
// 这里编译器根据 std::make_pair 所带参数的类型,自动推导出了函数模板的参数。
这个解决方案其实并不太理想,这是因为:
- 我们需要记住 make_pair, make_tuple 这类用于构造模板类的惯用法。
- 有些 make_XXX 函数在功能上并不等价于类模板的构造器,比如 make_shared 等等。
在C++17中,这个问题得到了解决,类模板构造器的模板参数同样能够被自动推导
std::pair a{1, "a"s}; // C++17
// 相当于
// std::pair<int, string> a{1, "a"s};
// 和函数模板一样,这里编译器根据 std::pair 构造器所带参数类型,自动推导出了构造器模板的参数。
由此我们不再需要 std::make_pair 之类的辅助函数了。
示例
#include <iostream>
#include <vector>
#include <functional>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
int main()
{
vector a = {1, 2, 3}; // C++17
// vector<int> a = {1, 2, 3}; // C++14
function f = [](int a){return a + 1;}; // C++17
// function<int(int)> f = [](int a){return a + 1;}; // C++14
tuple t{1, 2,5, "a"s}; // C++17
// tuple<int, double, string> t{1, 2,5, "a"s}; // C++14
// auto t = make_tuple(1, 2,5, "a"s); // C++14
sort(a.begin(), a.end(), greater{}); // C++17
// sort(a.begin(), a.end(), greater<>{}); // C++14
// sort(a.begin(), a.end(), greater<int>{}); // C++11
// map m = {{1, "a"s}, {2, "b"s}}; // {1, "a"s} 这种使用大括号的初始化列表没有类型
// 所以编译器无法自动推导 map 类模板的参数类型
map m = {pair{1, "a"s}, {2, "b"s}}; // C++17
// map<int, string> m = {{1, "a"s}, {2, "b"s}}; // C++14
}
以下内容来自视频 Class Template Argument Deduction
自定义类模板中的应用
template<typename T>
struct Container
{
Container(T* ptr) {} // 构造器 1
Container(T& v) {} // 构造器 2
Container(T const& v) {} // 构造器 3
template<typename D>
Container(T* ptr, D& deleter) {} // 构造器 4
};
struct Deleter {};
int main()
{
Container c{(int*)0}; // 调用构造器 1
int x; Container c2{x}; // 调用构造器 2
Container c3{0}; // 调用构造器 3
Deleter d;
Container c4{(int*)0, d}; // 调用构造器 4
// 以上编译器自动推导的结果都是 Container<int>
}
Automatic deduction guides(自动推断向导)
有些情况下,编译器无法对类模板的参数做出自动推导,比如下面这种模板参数类型是个嵌套类型的情况。
此时我们需要添加自动推断向导来帮助编译器来进行自动推导。
自动推断向导形式如下:
类模板名(参数列表) -> 类模板id
template<typename T>
struct Traits { using type = T; };
template<typename T>
struct Container
{
// 参数类型是嵌套类型,无法进行自动推导
Container(typename Traits<T>::type v) {}
};
// 自动推断向导
template<typename T>
Container(T) -> Container<T>;
int main()
{
Container c(0); // 编译器自动推导的结果是 Container<int>
}