C++模板编程(2)---非类型模板参数NonType Template Prameters

对函数模板和类模板而言,模板参数并不一定非要是类型不可,它们也可以是常规数值。当你以类型(types)作为template parameters时,程序代码中尚未决定的是类型;当你以一般数值(non-types)为模板参数时,程序中代码中待定的内容便是某些数值。使用这种模板,也就是本文所说的非类型模板时,必须明确指定数值,程序代码才得以实例化。本文将用非类型模板作一个类模板,也将展示如何在函数模板中使用非类型模板参数。

1. 非类型类模板参数

前面文章中提到了一个可变大小的堆栈类,其实还可以实现一个固定大小的数组来容纳元素。这样的好处是不必考虑诸如内存管理之类的问题。然而array大小的决定是一个比较困难的事情:array越小,堆栈容易溢满;array越大则容易造成空间浪费。一个可行的办法是让使用者指定array大小。这个大小也就是Stack的最大元素个数。

为了完成以上想法,我们应该把大小值当作一个template parameter:

1) 可以把这个非类型模板类TemplateStack的声明与定义放在一个头文件中:

//nonetypetemplate.h

#ifndef NONETYPETEMPLATE_H
#define NONETYPETEMPLATE_H
 
#include <stdexcept>
 
template <typename T, int MAXSIZE>
class TemplateStack {
private:
    T elements[MAXSIZE];
    int numElements;
public:
    TemplateStack();
    void push(T const&);
    void pop();
    T top() const;
    bool empty() const {
        return numElements == 0 ;
    }
};
 
//constructor
template  <typename T, int MAXSIZE>
TemplateStack<T, MAXSIZE>::TemplateStack()
    :numElements(0)
{
 
}
 
template <typename T, int MAXSIZE>
void TemplateStack<T,MAXSIZE>::push(T const& elem)
{
    if(numElements == MAXSIZE){
        throw std::out_of_range("TemplateStack<>::push:stack is full!");
    }
    elements[numElements] = elem;
    ++numElements;
}
 
template <typename T, int MAXSIZE>
void TemplateStack<T,MAXSIZE>::pop()
{
    if(numElements <= 0){
        throw std::out_of_range("TemplateStack<>::pop:stack is empty!");
    }
    --numElements;
}
 
template <typename T, int MAXSIZE>
T TemplateStack<T,MAXSIZE>::top() const
{
    if(numElements <= 0){
        throw std::out_of_range("TemplateStack<>::top:stack is empty!");
    }
    return elements[numElements - 1];
}
 
#endif // NONETYPETEMPLATE_H
 

2)使用这个模板类的文件放在主程序main.cpp中

//main.cpp

#include <QCoreApplication>
#include "nonetypetemplate.h"
#include <iostream>
#include <string>
#include <cstdlib>
 
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
 
    try {
        TemplateStack<int, 20> int20Stack;
        TemplateStack<int, 40>  int40Stack;
        TemplateStack<std::string, 40> string40Stack;
 
        int20Stack.push(7);
        std::cout << int20Stack.top() << std::endl;
        int20Stack.pop();
 
        string40Stack.push("hello,world");
        std::cout << string40Stack.top() << std::endl;
         string40Stack.pop();
         string40Stack.pop();
 
    } catch (std::exception const& ex) {
        std::cerr << "Exception: " << ex.what() << std::endl;
        return EXIT_FAILURE;
    }
 
    return a.exec();
}

3)运行结果

2. 非类型函数模板参数

也可以为函数模板定义非类型参数,例如下面的function template定义了一组函数,可以将参数x累加一个值VAL后传回:

template <typename T, int VAL>

T addValue(T const& x)

{

        return x + VAL;

}

当我们需要把函数或某种通用操作作为参数传递时,这一类函数就很有用。例如使用STL时,可以运用上述function template的实例,将某值加到元素集内的每个元素上:

std::transform(source.begin(), source.end(),   //来源端起止位置

                     dest.begin(),               //目标位置

                        addValue<int,5>);   //实际操作

最后一个自变量将函数模板addValue()实例化了,使其操作变成了加5.算法transform()会对 source中所有的元素调用这个具体现(函数),然后把结果传入dest中。

注意上面例子的一个问题:addValue<int, 5>是个函数模板(function Template)实体(instance),而所谓函数模板实体就是命名了一组重载函数集,即使该函数集内可能只有一个函数。根据目前标准,编译器无法借助重载函数集来进行模板参数的推导,不用我们把函数模板参数function template argument强制转型为精确类型(本例中第一个参数为整形,第二参数为常整形):

 std::transform(source.begin(), source.end(),

                                dest.begin(),

                                (int(*), (int const*)) addValue<int, 5>);   

)

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

非类型模板参数有某些局限:通常来说它们只能是常数整数(const intergral value), 包括enum, 或是指向外部链接(external linkage)对象的指针。

以浮动数或是class-type objects作为nontype template parameters是不可以的:

template <double VAT>   //错误:浮动值不能作为template paramters

double process (double v)

{

        return v *VAT;

}

template <std::string name>    //错误,class objects不能作为模板参数

class MyClass{

        ...

}

不允许浮动常数或简单的常量浮点表达式作为模板参数,其实只是历史因素,并非技术原因,或许将来会支持;

字串常数是一种采用内部链接(internal linkage)的对象,也就是不同模块内的两个同值的字符串常数,其实是不同的对象,因此不能用来作模板参数template arguments:

template <char const* name>

class MyClass{

...

};

MyClass<"hello"> x; //error:不能使用字符串常量

此外,全局指针也不能用作模板参数template argument:

template <char const* name>

Class MyClass{

...

}

char const *s = "hello";

MyClass<s> x ;

但是可以这么写:

template<char const* name>

Class MyClass {

...

};

extern char consts[] = "hello";

MyClass<s> x; //OK

全局的字符数组s(char Array)被初始化为“hello”,因此,s是一个外部链接对象(external linkage)

4. 小结

Template parameters 不仅仅只能时类型types,也可以是数值(values)。

注意得是:不能把浮动数、class-type对象、内部链接in非类型模板参数nontype template parameters的自变量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值