非类型模板参数
2015-09-22 21:57
399 查看
原文地址:博客园
这是《C++ Templates》第四章的学习笔记。这一章和下一章可以看作是对前面介绍的函数模板和类模板的补充。
这章的主要内容包括:
非类型的类模板参数。
非类型的函数模板参数。
非类型模板参数的限制。
在具体介绍以前有比较说明一下什么是非类型的模板参数。在前面简单介绍函数模板和类模板的时候,我们都再三说明,模板的参数应该是类型。我们需要通过指定某种类型来实例化一个模板。但是,在这一章,我们会看见一些值作为模板参数的情况。这里着重强调类型和值的区别。正因为是值作为模板参数,这一章才显得很特别。
一:非类型的类模板参数。
我认为类模板的非类型参数相当于一个全局常量的角色。书中举了下面的例子来说明非类型的类模板。这一章通过重新定义一个Stack模板,要求使用一个固定大小的数组作为元素的容器,并且数组的大小可以由模板的使用者自己定义。那么,对于模板的设计者,就应该提供一个接口使得使用者可以定义数组的大小。这就需要用到非类型的类模板参数。下面的代码能很好的解释这个问题:
1 #include <iostream>
2 #include <string>
3 #include <cstdlib>
4 #include <stdexcept>
5
6 template<typename T, int MAXSIZE>
7 class Stack{
8 private:
9 T elems[MAXSIZE];
10 int numElems;
11
12 public:
13 Stack();
14 void push(T const&);
15 void pop();
16 T top() const;
17 bool isEmpty() const{
18 return numElems == 0;
19 }
20
21 bool isFull() const{
22 return numElems == MAXSIZE;
23 }
24 };
25
26 template<typename T, int MAXSIZE>
27 Stack<T, MAXSIZE>::Stack():numElems(0)
28 {
29 // 不作任何事,仅为了初始化numElems。
30 }
31
32 template<typename T, int MAXSIZE>
33 void Stack<T, MAXSIZE>::push(T const& elem)
34 {
35 if(numElems == MAXSIZE)
36 {
37 throw std::out_of_range("Stack<>::push()==>stack is full.");
38 }
39
40 elems[numElems] = elem;
41 ++numElems;
42 }
43
44 template<typename T, int MAXSIZE>
45 void Stack<T, MAXSIZE>::pop()
46 {
47 if(numElems <= 0)
48 {
49 throw std::out_of_range("Stack<>::pop: empty stack");
50 }
51
52 --numElems;
53 }
54
55 template<typename T, int MAXSIZE>
56 T Stack<T, MAXSIZE>::top()const
57 {
58 if(numElems)
59 {
60 throw std::out_of_range("Stack<>::pop: empty stack");
61 }
62
63 // 返回最后一个元素。
64 return elems[numElems - 1];
65 }
66
67 int main()
68 {
69 try
70 {
71 Stack<int, 20> int20Stack;
72 Stack<int, 40> int40Stack;
73 Stack<std::string, 40> stringStack;
74
75 int20Stack.push(7);
76 std::cout<<"int20Stack.top() : "<<int20Stack.top()<<std::endl;
77 int20Stack.pop();
78
79 stringStack.push("HelloWorld!");
80 std::cout<<"stringStack.top() : "<<stringStack.top()<<std::endl;
81 stringStack.pop();
82 stringStack.pop();
83 }
84 catch(std::exception const& ex)
85 {
86 std::cerr<<"Exception: "<<ex.what()<<std::endl;
87
88 return EXIT_FAILURE;
89 }
90
91 return 0;
92 }
上面的代码揭示了非类型的类模板参数的定义和使用方法。需要注意的有:
非类型的类模板参数也是模板的参数之一。有某个非类型的模板参数的模板和没有那个非类型的模板参数的模板是两个不同的模板。
上面的int20Stack实例和int40Stack实例中,虽然实例化模板的时候都使用了int作为类型参数。但是他们仍然是两个不同的类的实例。他们之间没有任何的关系。不能相互进行类型转化,也不能赋值。这里需要注意,类模板的实例化在实质上是编译器将原来的模板拷贝一份,然后根据实例化参数替换原来的类型定义(也包括非类型的模板参数),从而成为一个新的类。也就是说,对于相同的实例化参数(包括非类型的模板参数)编译器都会实例化成为一个相同的类,只是我们不知道类名罢了。从这个角度上来说,上面的int20Stack和int40Stack是两个不同类的两个实例,他们之间没有任何关系,因为他们的模板实例化参数不一样。但是,如果定义为[Stack<int, 20> intStack1; Stack<int, 20> intStack2;]那么intStack1和intStack2可以相互赋值。因为他们对模板的实例化参数是一样的,从而都是同一类型的。这里有必要澄清一个事实,如果两个类有相同的成员函数和成员变量,他们生成的实例仍然是两个类型,与这里的情况完全不一样。
非类型的类模板参数也可以有缺省值。例如,template<typename T, int MAXSIZE =
100>...
再一次强调,非类型的模板参数和类型模板参数一样,也是标识一个模板的因素之一。
二:非类型的函数模板参数。
函数模板的非类型参数主要用来为函数提供一个运算常量。关于非类型的函数模板参数,书中有下面的例子:
1 //函数模板定义
2 template<typename T, int VAL>
3 T addValue(T const& x)
4 {
5 return x + VAL;
6 }
7
8 //其他代码
9
10
11 //函数模板的使用
12 std::transform(source.begin(), source.end(), dest.begin(),
13 (int(*) (int const&))addValue<int, 5>);
上面的代码中定义了一个函数模板,目的是对传入的参数加上一个指定的int型的5。这样的函数被普遍的使用在对一组数据进行同一处理的场合。例如,12行。这里需要注意的是:一std::transform函数本身就是一个模板函数,它的最后一个参数可以传递一个函数指针。因此,(int(*) (int const&))addValue<int, 5>其实是一个指向实例化后的addValue<T,
int VAL>模板函数的指针。至于这个指针怎么读,还请高手指教。另外需要注意的一点是,std::transform的最后一个参数不一定要是模板函数,任何函数都可以(关于std::transform的正确理解参考下面的评论)。只是模板函数更合适处理多种类型的数据罢了。
三:非类型模板参数的限制。
关于非类型模板参数的限制目前记住它可以是常整型(包括枚举类型)和指向外部连接对象的指针就可以可了。由于历史原因,浮点型不能作为非类型模板的参数;而指针和字符串作为非类型模板的参数是有条件的。我想这与变量的作用范围和生命周期有关吧。书中后面会有比较相信的介绍,就等到时候再细看了。
这是《C++ Templates》第四章的学习笔记。这一章和下一章可以看作是对前面介绍的函数模板和类模板的补充。
这章的主要内容包括:
非类型的类模板参数。
非类型的函数模板参数。
非类型模板参数的限制。
在具体介绍以前有比较说明一下什么是非类型的模板参数。在前面简单介绍函数模板和类模板的时候,我们都再三说明,模板的参数应该是类型。我们需要通过指定某种类型来实例化一个模板。但是,在这一章,我们会看见一些值作为模板参数的情况。这里着重强调类型和值的区别。正因为是值作为模板参数,这一章才显得很特别。
一:非类型的类模板参数。
我认为类模板的非类型参数相当于一个全局常量的角色。书中举了下面的例子来说明非类型的类模板。这一章通过重新定义一个Stack模板,要求使用一个固定大小的数组作为元素的容器,并且数组的大小可以由模板的使用者自己定义。那么,对于模板的设计者,就应该提供一个接口使得使用者可以定义数组的大小。这就需要用到非类型的类模板参数。下面的代码能很好的解释这个问题:
1 #include <iostream>
2 #include <string>
3 #include <cstdlib>
4 #include <stdexcept>
5
6 template<typename T, int MAXSIZE>
7 class Stack{
8 private:
9 T elems[MAXSIZE];
10 int numElems;
11
12 public:
13 Stack();
14 void push(T const&);
15 void pop();
16 T top() const;
17 bool isEmpty() const{
18 return numElems == 0;
19 }
20
21 bool isFull() const{
22 return numElems == MAXSIZE;
23 }
24 };
25
26 template<typename T, int MAXSIZE>
27 Stack<T, MAXSIZE>::Stack():numElems(0)
28 {
29 // 不作任何事,仅为了初始化numElems。
30 }
31
32 template<typename T, int MAXSIZE>
33 void Stack<T, MAXSIZE>::push(T const& elem)
34 {
35 if(numElems == MAXSIZE)
36 {
37 throw std::out_of_range("Stack<>::push()==>stack is full.");
38 }
39
40 elems[numElems] = elem;
41 ++numElems;
42 }
43
44 template<typename T, int MAXSIZE>
45 void Stack<T, MAXSIZE>::pop()
46 {
47 if(numElems <= 0)
48 {
49 throw std::out_of_range("Stack<>::pop: empty stack");
50 }
51
52 --numElems;
53 }
54
55 template<typename T, int MAXSIZE>
56 T Stack<T, MAXSIZE>::top()const
57 {
58 if(numElems)
59 {
60 throw std::out_of_range("Stack<>::pop: empty stack");
61 }
62
63 // 返回最后一个元素。
64 return elems[numElems - 1];
65 }
66
67 int main()
68 {
69 try
70 {
71 Stack<int, 20> int20Stack;
72 Stack<int, 40> int40Stack;
73 Stack<std::string, 40> stringStack;
74
75 int20Stack.push(7);
76 std::cout<<"int20Stack.top() : "<<int20Stack.top()<<std::endl;
77 int20Stack.pop();
78
79 stringStack.push("HelloWorld!");
80 std::cout<<"stringStack.top() : "<<stringStack.top()<<std::endl;
81 stringStack.pop();
82 stringStack.pop();
83 }
84 catch(std::exception const& ex)
85 {
86 std::cerr<<"Exception: "<<ex.what()<<std::endl;
87
88 return EXIT_FAILURE;
89 }
90
91 return 0;
92 }
上面的代码揭示了非类型的类模板参数的定义和使用方法。需要注意的有:
非类型的类模板参数也是模板的参数之一。有某个非类型的模板参数的模板和没有那个非类型的模板参数的模板是两个不同的模板。
上面的int20Stack实例和int40Stack实例中,虽然实例化模板的时候都使用了int作为类型参数。但是他们仍然是两个不同的类的实例。他们之间没有任何的关系。不能相互进行类型转化,也不能赋值。这里需要注意,类模板的实例化在实质上是编译器将原来的模板拷贝一份,然后根据实例化参数替换原来的类型定义(也包括非类型的模板参数),从而成为一个新的类。也就是说,对于相同的实例化参数(包括非类型的模板参数)编译器都会实例化成为一个相同的类,只是我们不知道类名罢了。从这个角度上来说,上面的int20Stack和int40Stack是两个不同类的两个实例,他们之间没有任何关系,因为他们的模板实例化参数不一样。但是,如果定义为[Stack<int, 20> intStack1; Stack<int, 20> intStack2;]那么intStack1和intStack2可以相互赋值。因为他们对模板的实例化参数是一样的,从而都是同一类型的。这里有必要澄清一个事实,如果两个类有相同的成员函数和成员变量,他们生成的实例仍然是两个类型,与这里的情况完全不一样。
非类型的类模板参数也可以有缺省值。例如,template<typename T, int MAXSIZE =
100>...
再一次强调,非类型的模板参数和类型模板参数一样,也是标识一个模板的因素之一。
二:非类型的函数模板参数。
函数模板的非类型参数主要用来为函数提供一个运算常量。关于非类型的函数模板参数,书中有下面的例子:
1 //函数模板定义
2 template<typename T, int VAL>
3 T addValue(T const& x)
4 {
5 return x + VAL;
6 }
7
8 //其他代码
9
10
11 //函数模板的使用
12 std::transform(source.begin(), source.end(), dest.begin(),
13 (int(*) (int const&))addValue<int, 5>);
上面的代码中定义了一个函数模板,目的是对传入的参数加上一个指定的int型的5。这样的函数被普遍的使用在对一组数据进行同一处理的场合。例如,12行。这里需要注意的是:一std::transform函数本身就是一个模板函数,它的最后一个参数可以传递一个函数指针。因此,(int(*) (int const&))addValue<int, 5>其实是一个指向实例化后的addValue<T,
int VAL>模板函数的指针。至于这个指针怎么读,还请高手指教。另外需要注意的一点是,std::transform的最后一个参数不一定要是模板函数,任何函数都可以(关于std::transform的正确理解参考下面的评论)。只是模板函数更合适处理多种类型的数据罢了。
三:非类型模板参数的限制。
关于非类型模板参数的限制目前记住它可以是常整型(包括枚举类型)和指向外部连接对象的指针就可以可了。由于历史原因,浮点型不能作为非类型模板的参数;而指针和字符串作为非类型模板的参数是有条件的。我想这与变量的作用范围和生命周期有关吧。书中后面会有比较相信的介绍,就等到时候再细看了。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性
- C++ Custom Control控件向父窗体发送对应的消息