C++ Templates学习笔记之:非类型模板参数

这是《C++ Templates》第四章的学习笔记。这一章和下一章可以看作是对前面介绍的函数模板和类模板的补充。
这章的主要内容包括:
  1. 非类型的类模板参数。
  2. 非类型的函数模板参数。
  3. 非类型模板参数的限制。
在具体介绍以前有比较说明一下什么是非类型的模板参数。在前面简单介绍函数模板和类模板的时候,我们都再三说明,模板的参数应该是类型。我们需要通过指定某种类型来实例化一个模板。但是,在这一章,我们会看见一些值作为模板参数的情况。这里着重强调类型和值的区别。正因为是值作为模板参数,这一章才显得很特别。

一:非类型的类模板参数。
我认为类模板的非类型参数相当于一个全局常量的角色。书中举了下面的例子来说明非类型的类模板。这一章通过重新定义一个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  }
上面的代码揭示了非类型的类模板参数的定义和使用方法。需要注意的有:
  1. 非类型的类模板参数也是模板的参数之一。有某个非类型的模板参数的模板和没有那个非类型的模板参数的模板是两个不同的模板。
  2. 上面的int20Stack实例和int40Stack实例中,虽然实例化模板的时候都使用了int作为类型参数。但是他们仍然是两个不同的类的实例。他们之间没有任何的关系。不能相互进行类型转化,也不能赋值。这里需要注意,类模板的实例化在实质上是编译器将原来的模板拷贝一份,然后根据实例化参数替换原来的类型定义(也包括非类型的模板参数),从而成为一个新的类。也就是说,对于相同的实例化参数(包括非类型的模板参数)编译器都会实例化成为一个相同的类,只是我们不知道类名罢了。从这个角度上来说,上面的int20Stack和int40Stack是两个不同类的两个实例,他们之间没有任何关系,因为他们的模板实例化参数不一样。但是,如果定义为[Stack<int20> intStack1; Stack<int20> intStack2;]那么intStack1和intStack2可以相互赋值。因为他们对模板的实例化参数是一样的,从而都是同一类型的。这里有必要澄清一个事实,如果两个类有相同的成员函数和成员变量,他们生成的实例仍然是两个类型,与这里的情况完全不一样。
  3. 非类型的类模板参数也可以有缺省值。例如,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的正确理解参考下面的评论) 。只是模板函数更合适处理多种类型的数据罢了。

三:非类型模板参数的限制。
关于非类型模板参数的限制目前记住它可以是常整型(包括枚举类型)和指向外部连接对象的指针就可以可了。由于历史原因,浮点型不能作为非类型模板的参数;而指针和字符串作为非类型模板的参数是有条件的。我想这与变量的作用范围和生命周期有关吧。书中后面会有比较相信的介绍,就等到时候再细看了。

转载于:https://www.cnblogs.com/zhaoxb1982/archive/2009/08/04/1538312.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值