非类型模板参看,顾名思义,模板参数不限定于类型,普通值也可作为模板参数。在基于类型的模板中,模板实例化时所依赖的是某一类型的模板参数,你定义了一些模板参数( template<typename T> )未加确定的代码,直到模板被实例化这些参数细节才真正被确定。而非类型模板参数,面对的未加确定的参数细节是指(value),而非类型。当要使用基于值的模板时,你必须显式地指定这些值,模板方可被实例化。

非类型类模板参数

这里我们使用一个新版本的Stack类模板,这类模板的底层容器是一个一维数组,数组的元素类型由模板类型参数 typename T 指定,而一位数组在初始化时必须指定其大小,这个大小便可通过一个非类型的模板参数 int MAXSIZE 指定,当然也可通过构造函数传递。

template<typename T, int MAXSIZE>
class Stack
public:
    Stack():idx(0){}
    bool empty() const { return idx == 0;}
    bool full() const { return idx == MAXSIZE;}
    void push(const T&);
    void pop();
    T& top();
    const T& top() const;
private:
    int idx; 
    T elems[MAXSIZE];
template<typename T, int MAXSIZE>
void Stack<T, MAXSIZE>::push(const T& elem)
    if (full())
        throw std::out_of_range("Stack<>::push(): full stack");
    elems[idx++] = elem;
template<typename T, int MAXSIZE>
void Stack<T, MAXSIZE>::pop()
    if (!empty())
        idx--;
        throw std::out_of_range("Stack<>::pop(): empty stack")
template<typename T, int MAXSIZE>
T& Stack<T, MAXSIZE>::top()
    if (empty())
        throw std::out_of_range("Stack<>::top(): empty stack");
    return elems[idx-1];
template<typename T, int MAXSIZE>
const T& Stack<T, MAXSIZE>::top() const
    if (empty())
        throw std::out_of_range("Stack<>::top(): empty stack");
    return elems[idx-1];

客户端程序:

Stack<int, 10> int10Stack; Stack<int, 20> int20Stack; int20Stack.push(7); catch(std::exception& ex) cout << ex.what() << endl; return EXIT_FAILURE;

每个模板实例都有自己的类型,int10Stackint20Stack属于不同的类型,这两种类型之间也不存在显示或隐式的类型转换。

同样地,也可以为非类型模板参数指定缺省值:

template<typename T, int MAXSIZE = 20>
class Stack

这样在调用时:

Stack<int> intStack;        // == Stack<int, 20>

非类型函数模板参数

同样地也可以为函数模板定义为非类型参数,如下的函数模板定义一组用于增加特定值的函数:

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

当然这样做纯粹为了演示,意义不大,我们可以将非类型参数的类型(这话有点绕)定义为类型参数:

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

千万不要小瞧这样的简单机制(整个计算机理论的恢弘大厦的本质也还是0/1呢),如果把函数(仿函数)或者某一操作当做参数传递给某一函数,这些实现了一些简单的功能的函数将产生巨大的作用。例如,借助标准模板库(STL),可以将这个函数模板的一个实例传递给集合中的每一个元素,将集合中的每一个值都增加一个预设的值。

#include <algorithm>
std::vector<int> ivec;
std::transform(src.begin(), src.end(),      // 原容器(待转换)的起点和终点 
                    dst.begin(),            // 目标容器的起点
                    addValue<std::vector<int>::value_type, 10>);    // 操作或者函数(也可以是仿函数)

这里要想成为STL <algorithm>算法中某一函数的参数(尤其是在内部作为函数使用的),需要满足一定的要求,比如本例中的std::transform(),传递进来的在内部作为函数使用的第四个参数,只能接受一个参数,且需返回一个值(返回值的类型就是目标容器的元素类型),这是基本要求。

另外还有一个问题需要注意,addValue<int, 5>是一个函数模板的实例化版本,而函数模板的实例化通常被视为用来命名一组重载函数的集合(即使该集合中只有一个元素)。在一些较老的C++标准里,重载函数的集合不能用于模板参数(如本例的transform())的演绎。于是,必须显式地将函数模板的实参强制转换为具体的类型:

std::transform(ivec.begin(), ivec.end(), dst.begin(), (int(*)(const int& ))addValue<int, 5>);

一个完整的演示程序即是:

int arr[] = {1, 2, 3, 4, 5};
vector<int> src(arr, arr+5), dst(5);
typedef vector<int>::value_type value_type;
transform(src.begin(), src.end(), dst.begin(), 
            (value_type (*)(const value_type&)addValue<value_type, 5>);
copy(dst.begin(), dst.end(), ostream_iterator<value_type>(cout, " "));
        // ostream_iterator 在<iterator>的std命名空间中

非类型模板参数的限制

非类型模板参数是有类型限制的。一般而言,它可以是常整数(包括enum枚举类型)或者指向外部链接对象的指针

浮点数和类对象(class-type)不允许作为非类型模板参数:

template<double VAL>            // ERROR: 浮点数不可作为非类型模板参数
double process(double v)
    return v * VAL;
template<std::string name>      // ERROR:类对象不能作为非类型模板参数
class MyClass

稍作变通,我们即可使编译通过:

template<double* PVAL>
double process(const double& x)
    return x * (*PVAL);
template<const char* name>
class MyClass

这样可顺利通过编译,但如果想在当前文件中使用这两个模板,还需要动一些手脚:

double val = 10;
double res = process<&val>(20);     // ERROR: 表达式必须含有常量值
MyClass<"hello"> x;                 // ERROR: 模板参数不能引用非外部实体
const char* s  = "hello";
MyClass<s> x;                       // ERROR: 表达式必须含有常量值

这里就点出另外一点注意事项,也就是非类型模板参数的限制,非类型模板参数可以是指针,但该指针必须指向外部链接对象,还记得在A.cpp中如何引用B.cpp中的全局变量吗,在A.hpp中使用extern关键字对外部变量加以引用。

// B.cpp
double val = 3.14159265;
char str[] = "hello";
// A.hpp
extern double val;
extern char str[];
// A.cpp
#include "A.hpp"
double res = process<&val>(10);
MyClass<str> x;
最近打算挑选几个STL容器做个简单实现,发现里面牵涉到不少模板知识。这里算提前学习一下C++模板的相关知识吧。这次主要学习了什么是函数模板(这个最简单),类模板以及类型模板参数。下面挨个举例说明。 文章目录1. 函数模板2. 类模板3. 类型模板参数 1. 函数模板 函数模板是最简答的一个,下面就定义一个返回两个值中最大者的函数模板: namespace og { template&amp;amp;lt;...
// std::template 可变参数模板 //可变模板中,省略号的作用有两个: //1.声明一个参数包T... args,这个参数包中可以包含0到任意个模板参数; //2.在模板定义的右边,可以将参数包展开成一...
整 按尾(常 整&i){整 j=i%10;中 j;} //类型参数:有用的,谁说没用. //整型常量(包括枚)或指向外部链接指针(包括函数指针,类成员函数指针,及具有外部链接的字符串常量指针). //类似(外 常 符* 名="abc"),是可以作模板参数的 元<型名 T,整(*f)(常 T&)> 构 比{//函数名,用作模板参数 极 符号()(常 T&...