• 非类型类模板参数
  • 非类型函数模板参数
  • 非类型模板参数的限制

非类型模板参看,顾名思义,模板参数不限定于类型,普通值也可作为模板参数。在基于类型的模板中,模板实例化时所依赖的是某一类型的模板参数,你定义了一些模板参数(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--;elsethrow 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];
}

客户端程序:

try
{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;

C++基础——非类型模板参数相关推荐

  1. C++基础::非类型模板参数在STL中的应用

    bitset template<size_t N> class bitset{ } tuple_element 其源码也较为简单,不妨全部列出: template<size_t id ...

  2. C++ 泛型编程(二):非类型模板参数,模板特化,模板的分离编译

    目录 非类型模板参数 函数模板的特化 类模板的特化 全特化 偏特化 部分参数特化 参数修饰特化 模板分离编译 问题分析 解决方法 非类型模板参数 模板的参数分为两种,一种是非类型参数,一种是类型参数. ...

  3. 浅谈非类型模板参数、模板的特化

    非类型模板参数 1.模板参数分类类型形参与非类型形参. 2.类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称.类型参数也可以给缺省值 3.非类型形参,就是用一个常 ...

  4. C++非类型模板参数

    对于函数模板与类模板,模板参数并不局限于类型,普通值也可以作为模板参数.在基于类型参数的模板中,你定义了一些具体的细节来加以确定代码,直到代码被调用时这些细节才被真正的确定.但是在这里,我们面对的是这 ...

  5. C++知识点60——非类型模板参数

    模板参数不仅可以使类型,也可以是值.可以用特定的类型名(比如int)而非typename或者class表示非类型模板参数.当模板被实例化时,非类型模板参数会被一个值而不是类型替代.非类型模板参数 一. ...

  6. java两个函数名字相同_为什么C不允许两个具有相同名称的函数/类模板,只有非类型模板参数(整数类型)的类型不同?...

    我尝试这个时编译器会出错 . 我试过VC和g . 这同样适用于函数模板和类模板(但对于函数模板,只有在实例化函数模板时才会出现编译器错误;当编译器遇到第二个类定义时,会立即出现类模板的编译器错误) . ...

  7. C++17 非类型模板参数的auto 和 DevC++ 支持C++17

    模板参数分为两种,一种是类型模板参数,也是我们用得最多的一种: template <typename T, typename U> auto add(T t, U u) {     ret ...

  8. 非类型模板参数(参考《C++ Templates 英文版第二版》)

    非类型模板参数(参考<C++ Templates 英文版第二版>) Chapter 3 3.1 非类型类模板参数 与前几章的简单例子不同,你也可以通过std::array实例化一个固定大小 ...

  9. C++模板(函数模板,类模板)的基本使用与非类型模板参数与模板的特化

    C++模板 模板初阶 泛型编程 函数模板 函数模板概念 函数模板格式 函数模板的原理 函数模板的实例化 隐式实例化 显式实例化:在函数名后的<>中指定模板参数的实际类型 模板参数的匹配原则 ...

最新文章

  1. 机器学习+优化问题的种类、如何优化、凸优化、非凸优化、对偶问题、KKT条件
  2. 需求工程-软件需求模式读书笔记3
  3. 关于Topic和Partition
  4. 【转】详解Windows消息分类以及WM_COMMAND与WM_NOTIFY的区别,以及模拟发送控件通知消息
  5. Python 正则模块的应用
  6. Spring boot :使用 Swagger 2 构建 RESTful APIs
  7. c语言const 修饰二级指针,C++中const修饰二级指针(从类型‘int**’到类型‘const int**’的转换无效)...
  8. 机器学习(3)——K-近邻算法改进约会网站的配对效果实例
  9. 中介效应分析及R实现
  10. 基于Jtopo的网络拓扑编辑器初探
  11. 理财 股票 K线分析
  12. 纯css按钮代码,纯CSS实现3D按钮效果实例代码
  13. 计算机公共课4-电子表格系统 Excel 2010
  14. laravel安装laravel-s
  15. html gif 蒙层,使用蒙层实现启动图动画效果
  16. Kubernetes - 一键安装Kubernetes集群
  17. 右键菜单,下拉选项的截屏
  18. Android 轻松连接 Usb Device —— UsbConnector
  19. 用计算机名搜不到共享打印机,win7电脑共享了打印机,但是别的电脑却搜不到。...
  20. ModuleNotFoundError: No module named ‘xxx‘; ‘xxx‘ is not a package解决

热门文章

  1. npm install cracoc 老版本_npmamp;nvmamp;nrmamp;yarn常用的命令和常见的手法
  2. TensorFlow安装教程(Windows/Linux两种版本)
  3. 基于内容的图像检索概述
  4. Network 第九篇 - 双机热备-HSRP
  5. 小技巧处理div内容溢出
  6. HTML5 Canvas渐进填充与透明(摘自 http://blog.csdn.net/jia20003/article/details/9251893)
  7. java的反射机制(第二篇)
  8. 获取一些系统和用户的通用属性
  9. 基于visual Studio2013解决C语言竞赛题之1094纵横图
  10. ORA-00257: archiver error. Connect internal only, until freed——解决