1.4类型推导(type derivation)-1.4.5-类模板参数推导(CTAD)

类模板参数推导是c++17进一步引入的减少冗余代码的重要特性。当我们使用标准库里提供的模板函数,或许不知道他就是一个模板函数,但使用起来却和普通函数一样自然。

int foo = std::max(1,2); //忽略模版参数的指明无<>
double bar = std::max<double>(1,2.0); //指明模版参数:<double>

标准库里面提供的max函数用于求任意两个数值类型的最大值,当两个数值类型一致时,编译器会帮我们推导出函数模板的模版参数

例如:

对于第一个版本,编译器会实例化版本max<int>

对于第二个版本,编译器不知道该用哪个类型,因此需要我们显式指定模版参数,从而实例化版本max<double>

然而我们在使用模板类时,都必须指名模版类的模版参数后才能使用。

std::vector<int> foo {1,2,3,4};
std::pair<int, double> bar {1,2,0};//pair为序对

初始化一个序对类型时,我们明显可以通过构造传递的两个值的类型得到pair<int, double>,但是在c++17前,尽管编译器知道这些类型,仍需显式指名模版类的模版参数。标准库也发现了这一点,提供了一些辅助模版参数来解决这个问题

auto bar = std::make_pair(1,2.0);

make_pair是个模板函数,可以自动推导出函数的两个参数类型,从而构造出最终的序对类型。对程序员来说,每写一个模板类都需要考虑封装一个辅助函数来构造这个类型的对象。这是件重复的,繁琐的事情,还要考虑转发引用与完美转发的问题。

c++17起引入的这个CTAD特性就是为了解决模板类需求显式指明模版参数的问题。而且这个特性不是很复杂,我们可以像使用普通类一样使用模版类,就像模版函数与普通函数表现的一致性。在c++17中,std::pair{1,2.0}与std::pair<int, double>{1,2.0}等价.通过改写前面的代码,可以看到明显的区别,改写后的代码更精简。

std::vector foo{1,2,3,4};
std::pair bar{1,2.0};

考虑用户实现一个模板类,最简单的情况下,只要模板类参数和构造函数能够对应的上,对于编译器来说没有任何歧义,构造的时候就能推导出类型

template<typename T,typename U>
struct Pair{
  Pair();
  Pair(T,U);
  //...
};
Pair foo{1,2};//编译器能够自动推导出Pair<int, int>

在这个例子中,在构造Pair时编译器能够正确地通过模板参数推导出其构造函数的模板参数,同时将模板参数运用于最终的模板类上。然而当使用默认方式构造Pair{}时将触发编译错误,因为编译器无法得知这两个模板参数,Pair{}也没有提供默认的模板参数。

当构造函数与模板类参数无法对应时,这时候需要程序员定义一些推导规则,编译器会优先考虑推导规则

template<typename T, typename U>
struct Pair{
 template<typename A, typename B>
 pair(A&&, B&&);
 //...
};
Pair foo{1,2}; //编译错误!

这个例子在前面稍微不同,构造函数与模版类的模板参数不一致,编译器会认为T与U,A与B是不同的类型,因此无法通过构造函数得知最终的模版类参数。这时可以通过添加推导规则来解决这个问题。

template<typename T, typename U>
Pair(T,U)->Pair<T,U>

在做模板参数推导时,推动规则拥有很高的优先级,编译器会优先考虑推导规则,之后才考虑通过构造函数来构造。

上述规则相当于告诉编译器,当通过诸如Pair(T,U)的方式构造程序时,自行推导出模版类Pair<T,U>。因此Pair{1,2}会得到正确的Pair<int ,int>类型。确定模板类后,并不会影响构造函数的决议过程,就和程序员显式指定模版参数一样。在这个例子中会通过转发引用版本的构造函数进一步构造。

推导规则和函数声明的返回类型后置写法类似,需要注意的是推导规则前面没有auto,通过这一点来区分两者。CTAD特性与推导规则时非侵入式的,为已有的模板类添加推导规则并不会破坏已有的显式指明的代码,这也是为何标准库在引入特性之后不会破坏用户代码的原因。

模板函数的模板参数可以在程序员使用时自动对类型进行推导,从而实例化进行调用。而在c++17之前,使用模板类需要显式指名模板参数,而在c++17后提供的CTAD特性可以简化这一点,同时提供自定义推导规则以帮助编译器推导正确的类型。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部