1.3 整型也可是Template参数

优质
小牛编辑
127浏览
2023-12-01

模板参数除了类型外(包括基本类型、结构、类类型等),也可以是一个整型数(Integral Number)。这里的整型数比较宽泛,包括布尔型,不同位数、有无符号的整型,甚至包括指针。我们将整型的模板参数和类型作为模板参数来做一个对比:

template <typename T> class TemplateWithType;
template <int    V> class TemplateWithValue;

我想这个时候你也更能理解 typename 的意思了:它相当于是模板参数的“类型”,告诉你 T 是一个 typename

按照C++ Template最初的想法,模板不就是为了提供一个类型安全、易于调试的宏吗?有类型就够了,为什么要引入整型参数呢?考虑宏,它除了代码替换,还有一个作用是作为常数出现。所以整型模板参数最基本的用途,也是定义一个常数。例如这段代码的作用:

template <typename T, int Size> struct Array
{
  T data[Size];
};

Array<int, 16> arr;

便相当于下面这段代码:

class IntArrayWithSize16
{
  int data[16]; // int 替换了 T, 16 替换了 Size
};

IntArrayWithSize16 arr;

其中有一点需要注意,因为模板的匹配是在编译的时候完成的,所以实例化模板的时候所使用的参数,也必须要在编译期就能确定。例如以下的例子编译器就会报错:

template <int i> class A {};

void foo()
{
  int x = 3;
  A<5> a; // 正确!
  A<x> b; // error C2971: '_1_3::A' : template parameter 'i' : 'x' : a local variable cannot be used as a non-type argument
}

因为x不是一个编译期常量,所以 A<x> 就会告诉你,x是一个局部变量,不能作为一个模板参数出现。

嗯,这里我们再来写几个相对复杂的例子:

template <int i> class A 
{
public:
  void foo(int)
  {
  }
};
template <uint8_t a, typename b, void* c> class B {};
template <bool, void (*a)()> class C {};
template <void (A<3>::*a)(int)> class D {};

template <int i> int Add(int a)	// 当然也能用于函数模板
{
  return a + i;
}

void foo()
{
  A<5> a;
  B<7, A<5>, nullptr>	b; // 模板参数可以是一个无符号八位整数,可以是模板生成的类;可以是一个指针。
  C<false, &foo> c;    // 模板参数可以是一个bool类型的常量,甚至可以是一个函数指针。
  D<&A<3>::foo> d;     // 丧心病狂啊!它还能是一个成员函数指针!
  int x = Add<3>(5);   // x == 8。因为整型模板参数无法从函数参数获得,所以只能是手工指定啦。
}

template <float a> class E {}; // ERROR: 别闹!早说过只能是整数类型的啦!

当然,除了单纯的用作常数之外,整型参数还有一些其它的用途。这些“其它”用途最重要的一点是让类型也可以像整数一样运算。《Modern C++ Design》给我们展示了很多这方面的例子。不过你不用急着去阅读那本天书,我们会在做好足够的知识铺垫后,让你轻松学会这些招数。