1.4 模板偏特化

偏特化

前面介绍过,模板的偏特化可以有两种形式

template<typename T, int N>
struct Vec{
T v_[N];
};
template<>
struct Vec<float, 4> {
float v_[4];
};
template<int N>
struct Vec<bool, N>{
char v_[(N+sizeof(char)-1)/sizeof(char)];
};
  • 把所有的参数具体化,比如T = float, N = 4
  • 把部分参数具体化,比如T = bool

需要注意的是,已经被具体化的参数就不要再出现在template<>这里面了。如果所有的参数都被具体化的,那么template<>这里面就什么都没有了。

比较复杂的是两种情况,也就是模板特化与模板模板参数混在一起的时候。

#include <iostream>
#include <typeinfo>
using namespace std;
template<class T, class U> class A {
public:
T t_;
U u_;
};
template<class U> class A<int, U> {
public:
short x;
};
// V这里的参数只能从T/U/X里面选择。
template<typename T, typename U, typename X, template<typename, typename> class V> class B {
public:
V<T, U> v_;
void print() {
cout << "B" << endl;
}
};
B<int, int, float, A> c;
int main() {
c.print();
}

接着再分析另外一个例子。

template<typename T, int i> class Arg; // 用于模板型模板参数
// 通例
template<typename T1, typename T2, int i, template<typename, int> class CP>
class Example { };
// 完全特例化
template<>
class Example<int, float, 2, Arg>;
// 第一个参数有const修饰
template<typename T1, typename T2, int i, template<typename, int> class CP>
class Example<const T1, T2, i, CP> { };

需要注意的是后面的那个class CP在选择类型的时候,只能从T1/T2/i里面选择了。

类型组合的特化

再看一个有趣的例子。

// 第一二个参数为Arg的实例且满足一定关系,第四个参数为Arg
template<typename T, int i>
class Example<Arg<T, i>, Arg<T, i+10>, i, Arg> { };

也就是说,在特化的时候,可以写成这样。

template<参数范围A>
class Example<由A范围组合出按照顺序满足条件的这个具体类型即可> { };

那么在类里面使用Arg的时候,可以选择的参数范围就是已知的类型。比如:

template<typename T, int i> class Arg{ }; // 用于模板型模板参数
// 通例
template<typename T1, typename T2, int i, template<typename, int> class CP>
class Example { };
template<typename T, int i>
class Example<const T, Arg<T,i+10>, i, Arg> {
public:
Arg<T, i> s_;
Arg<const T, i> a_;
Arg<Arg<T,i>,i> b_;
};

所以,可以看出Arg这个模板类可以使用T,i, const T, Arg<T,i+10>, i,都可以。

关于模板特化

  • Base模板需要提前声明
  • 如果模板A是模板B的子集,优先匹配A。
  • 如果两个模板有交集,且不成子集关系,编译报错。

模板偏特化元编程

可以利用偏特化来实现if/else对类型的操作。比如判断两个类型是否相等。代码如下:

#include <iostream>
template<typename T1, typename T2>
struct is_same {
static constexpr bool value = false;
};
template<typename T>
struct is_same<T,T> {
static constexpr bool value = true;
};
// 自定义类型,只是用来测试is_same模板
template<typename T, int N>
struct Example { };
int main(void) {
typedef unsigned int uint;
typedef uint uint2;
std::cout << is_same<unsigned, uint2>::value << std::endl;
std::cout << is_same<Example<unsigned, 2>, Example<uint2, 2>>::value << std::endl;
std::cout << is_same<Example<int, 2>, Example<int, 3>>::value << std::endl;
return 0;
}

计算阶乘

#include <iostream>
#include <stdint.h>
template<int N>
struct order {
static constexpr int64_t value = N * order<N-1>::value;
};
// 由于只有一个参数需要偏特化,所以
// 这里用template<>
template<>
struct order<0> {
static constexpr int64_t value = 1;
};
int main(void) {
std::cout << order<10>::value << std::endl;
return 0;
}