《C++Templates》笔记——3.非类型模板参数

对于函数模板和类模板,模板参数并不局限于类型普通值也可以作为模板参数。

1.非类型的类模板参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdexcept>

template <typename T, int MAXSIZE>
class Stack{
private:
int elems[MAXSIZE]; //包含元素数组
int numElems; //元素的当前个数
public:
Stack(); //构造函数
void push(T const&); //压入元素
void pop(); //弹出元素
T top() const; //返回栈顶元素
bool empty() const { //返回栈是否为空
rerurn numElems == 0;
}
bool full() const { //返回栈是否已满
return numElems == MAXSIZE;
}
};

//构造函数
template <typename T, int MAXSIZE>
Stack<T, MAXSIZE>::Stack() : numElems(0)
{

}

//压入元素
template <typename T, int MAXSIZE>
void Stack<T, MAXSIZE>::push(T const& elem)
{
if (numElems == MAXSIZE){
trow std::out_of_range("Stack<>::push(): Stack is full!");
}
elems[numElems] = elem; //附加元素
++numElems; //增加元素的个数
}

//弹出元素
template <typename T, int MAXSIZE>
void Stack<T, MAXSIZE>::pop()
{
if (numElems <= 0){
trow std::out_of_range("Stack<>::push(): empty Stack!");
}
--numElems; //减少元素的个数
}

//返回栈顶元素
template <typename T, int MAXSIZE>
T Stack<T, MAXSIZE>::top() const
{
if (numElems <= 0){
trow std::out_of_range("Stack<>::push(): empty Stack!");
}
return elems[numElems-1]; //返回最后一个元素
}

为了使用上述模板,必须同时指定元素的类型和个数(即栈的最大元素容量)。

1
2
3
Stack<int, 20> int20Stack;
Stack<int, 40> int40Stack;
Stack<std::string, 40> stringStack;

当然,上面的模板同样可以为模板参数设置缺省值:

1
2
3
4
template <typename T = int, int MAXSIZE = 99>
class Stack{
......
}

2.非类型的函数模板参数

你也可以为函数模板定义非类型参数。例如:

1
2
3
4
5
template<typename T, int VAL>
T addValue(T const& x)
{
return x + VAL;
}

如果需要把函数或者操作用作参数的话,这类函数就相当有用。借助于标准模板库(STL),可以传递这个函数模板的实例化给集中的每一个元素,让他们都增加一个整数值:

1
2
3
std::transform(source.begin(), source.end(),            //源集合的起点和终点   
dest.begin(), //目标集合的起点
(int(*)(int const&))addValue<int, 5>; //操作(或者函数)

上面的调用,最后一个实参实例化的函数模板addValue(),它上int元素加5。源集合source中的每一个元素都会加5将结果存在目标集合中。下面是一个使用std::transform()函数的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "Stack.hpp"
#include <iostream>
#include <vector>
#include <algorithm>

template<typename T, int VAL>
T addValue(T const& x)
{
return x + VAL;
}

int main()
{
std::vector<int> source;
source.push_back(1);
source.push_back(2);
source.push_back(3);

std::vector<int> dest;
dest.resize(source.size()); //这一句不能少!!!

std::transform(source.begin(), source.end(),
dest.begin(),
addValue<int, 5>);
for (int i = 0; i < dest.size(); i++)
{
std::cout << dest[i] << " ";
}

system("pause");
return 0;
}

运行结果:6 7 8

3.非类型模板参数的限制

非类型模板参数是有限制的,通常是常整数(包括枚举值)或者指向外部链接对象的指针
浮点数和类对象是不允许作为非类型模板参数

1
2
3
4
5
6
7
8
9
10
template<double VAT>
double process(double v) //error:浮点数不能作为非类型模板参数
{
return V * VAT;
}

template<string name> //error:类对象不能作为非类型模板参数
class MyClass {
...
};

由于字符串文字是内部链接对象(因为两个具有相同名称但出于不同模块的字符串,是两个不同的对象),所以不能使用它们作为模板实参。
不能使用全局指针作为模板参数

1
2
3
4
5
6
7
template<char const* name>
class MyClass {
...
};

char const s[] = "hello";
MyClass<s> x; //s是一个指向内部链接对象的指针

但是我们可以这样来使用:

1
2
3
4
5
6
7
template<char const* name>
class MyClass {
...
};

extern char const s[] = "hello";
MyClass<s> x; //OK

全局字符数组s由”hello”初始化,是一个外部链接对象。

4.总结

  • 模板参数不但可以是类型,也可以是值。
  • 非类型模板参数,不能使用浮点数、class类的对象和内部链接对象(例如string)作为实参。

详情参考:《C++Templates》

-------------本文结束感谢您的阅读-------------