在 C++17 之前,
通过向模板类的构造函数传递参数无法推导出类的模板参数类型.
一般要通过一个帮助函数来构造一个对象,
通过传递给帮助函数的参数推导出参数类型,
然后在帮助函数中返回一个构造好的对象. 下面介绍 C++17 之前的做法与
C++17 的便利性.
template <typename T1, typename T2, typename T3>
class my_class {
T1 t1;
T2 t2;
T3 t3;
public:
explicit my_class(T1 t1_, T2 t2_, T3 t3_)
: t1{t1_}, t2{t2_}, t3{t3_}
{}
};
要构造 my_class 的对象, 可以通过下面的方法
my_class<int, int, float> obj{1, 2, 3.0};
通过上面这种方法, 当使用时要指定一堆的模板参数, 使用不太方便,
因此可以定义一个帮助函数, 比如 make_my_class
my_class make_my_class(T1 t1, T2 t2, T3 t3)
{
return {t1, t2, t3};
}
之后就可以通过下面的方法构造对象 my_obj, 可以得到模板类类型为
my_class
.
auto my_obj (make_my_class(1, 2, 3.0));
在 C++17 中, 可以直接通过构造函数的参数类型来推导出模板参数类型.
因此可以象下面的方法构造对象 obj, 可以得到模板类类型为
my_class
.
my_class obj(1, 2, 3.0);
虽然 C++17 利用构造函数的参数类型来推导模板类型使用方便, 但有时候也无法推导,
考虑下面的类定义
// 模板类定义, 用于求和, 和存储于成员变量 value 中
T>
struct sum {
T value;
template ... Ts>
sum(Ts&& ... values) : value{(values + ...)} {}
};
// 如果传递给构造函数的参数类型都相同, 可以推导出 T 的类型即为参数类型
sum my_sum{1, 2, 3}; // sum::value 为 6
// 如果传递给构造函数的参数类型不同, 则会编译错误
// sum my_sum{1, 2, 3.0}
当然, 传递给 sum 构造函数的还可能会是 char*
和 std::string
.
为了使用其能正常工作, 需要寻找出众多参数类型中的最普通类型, 可以利用
std::common_type_t<...>
. 在上面代码的基础上增加一个更加泛化的版本
// (1)
T>
struct sum {
T value;
template ... Ts>
sum(Ts&& ... values) : value{(values + ...)} {}
};
// (2)
template ... Ts>
sum(Ts&& ... ts) -> sum>;
此时, 如果传递给构造函数的参数类型相同, 直接会调用 (1) 的版本.
如果传递给构造函数的参数不同, 会调用 (2) 版本. 比如
// 得到 int, int, double 三种类型的普通类型为 double
// 最终会推导出一个 sum 的结构类型
sum int_sum{1, 2, 3};
// 得到 std::string, const char* 更普通的类型为 std::string
// 最终会推导出一个 sum 的结构类型
// sum ::value 为 std:string("abcdef")
sum string_sum {std::string{"abc"}, "def"};
从 C++17 开始, 可以很方便的构造一个模板类对象, 从而活动一个帮助函数. 在 C++
标准库中, 很多 make_XXX(...)
的帮助函数, 如 std::make_pair(),
std::make_tuple(), 在 C++17 之后, 这些帮助函数都会成为 deprecated 状态,
只是为前向兼容而保留.