非类型模板参数 和 模板型模板参数
整数以及枚举类型;指向对象或者函数的指针;对对象或函数的引用;指向对象成员的指针。统称为非类型模板参数。
模板型模板参数,是指模板参数还可以是一个模板。
 
1、整数模板参数
非类型模板参数的作用相当于为函数模板或类模板预定义一些常量,在生成模板实例时,也要求必须以常量即编译期已知的值为非类型模板参数赋值。//就是模板中有一个参数,但它并不是模板参数,并不会适配不同的类型,而是某种固定的类型 那么他的好处在哪?“模板中声明的常量,在模板的所有实例中都具有相同的值,而非类型模板参数则对于在不同的模板实例中拥有不同的值来满足不同的需求”。这句话说得太官方了,我们来看个例子:
  1. template<typename T>
  2. class CArray
  3. {
  4. static cosnt unsigned size = ;
  5. T elems[size];
  6. public:
  7. T& operator[](unsigned i) throw (std::out_of_range)
  8. {
  9. if (i >= size)
  10. {
  11. throw std::out_of_range("Access out of range\n");
  12. }
  13. else
  14. {
  15. return elems[i];
  16. }
  17. }
  18. };
这个例子存在什么问题?问题就在于数组的大小被写死了,这个模板在编译器只能灵活适配不同的数组类型,但是无法适配不同的数组大小。但是如果改成这样,就会灵活很多:
  1. template<typename T, unsigned Size>
  2. class CArray2
  3. {
  4. T elems[Size];
  5. public:
  6. T& operator[](unsigned i) throw (std::out_of_range)
  7. {
  8. if (i >= size)
  9. {
  10. throw std::out_of_range("Access out of range\n");
  11. }
  12. else
  13. {
  14. return elems[i];
  15. }
  16. }
  17. };
让我们来验证一下,非类型模板参数Size的值不同,是否产生的是不同的函数实例,稍微改造一下函数如下:

  1. template<typename T, unsigned Size>
  2. class CArray2
  3. {
  4. public:
  5. CArray2()
  6. {
  7. id++;
  8. }
  9. ~CArray2(){}
  10.  
  11. T elems[Size];
  12. public:
  13. T& operator[](unsigned i) throw (std::out_of_range)
  14. {
  15. if (i >= size)
  16. {
  17. throw std::out_of_range("Access out of range\n");
  18. }
  19. else
  20. {
  21. return elems[i];
  22. }
  23. }
  24.  
  25. public:
  26. static int id;
  27. };
  28.  
  29. template<typename T, unsigned Size> int CArray2<T, Size>::id = ; //顺便我们也应该了解这种带有非类型模板参数的模板类如何定义一个static成员
  30.  
  31. void main()
  32. {
  33. CArray2<char, > array0;
  34. printf("ID:%d\n", array0.id);
  35.  
  36. CArray2<char, > array1;
  37. printf("ID:%d\n", array1.id);
  38.  
  39. CArray2<int, > array3;
  40. printf("ID:%d\n", array3.id);
  41.  
  42. CArray2<int, > array4;
  43. printf("ID:%d\n", array4.id);
  44.  
  45. getchar();
  46. }
运行结果如下:

 
2、函数指针模板参数
前面一小节,在模板参数中固定写死了某种数据类型,这里我们也可以在定义模板时固定写死某种函数参数类型。而这个函数参数类型又可以适配模板中的模板参数类型。

  1. template<typename T, void(*f)(T &v)>
  2. void foreach(T array[], unsigned size)
  3. {
  4. for (unsigned i = ; i < size; ++i)
  5. {
  6. f(array[i]);
  7. }
  8. }
  9.  
  10. template<typename T>
  11. void inc(T &v){ ++v; }
  12.  
  13. template<typename T>
  14. void dec(T &v){ --v; }
  15.  
  16. template<typename T>
  17. void print(T &v){ printf("%d ", v); }
  18.  
  19. void main()
  20. {
  21. int array[] = { , , , , , , , };
  22. foreach<int, print<int>>(array, );
  23.  
  24. foreach<int, inc<int>>(array, );
  25.  
  26. getchar();
  27. }
 
3、指针及引用模板参数
只有指向全局变量及外部变量及类静态变量的指针及引用才可以作为模板参数。函数的局部变量、类成员变量等均不能作为模板参数。因为模板参数值必须是编译时已知的。

  1. template<int* p>
  2. struct wrapper
  3. {
  4. int get(){ return *p; }
  5. void set(int v){ *p = v; }
  6.  
  7. };
  8.  
  9. template<int &p>
  10. struct wrapper2
  11. {
  12. int get(){ return p; }
  13. void set(int v){ p = v; }
  14. };
  15.  
  16. int global_variable = ;
  17.  
  18. int main()
  19. {
  20. wrapper<&global_variable> gwrapper;
  21. wrapper2<global_variable> gwrapper2;
  22. }
有一个明确的结论是,global_variable 决定了gwrapper的类型。即如果我添加一个
int global_variable3 = 0; 和 wrapper<global_variable3> gwrapper3;
那么gwrapper和gwrapper3并非同一个类型。
之前我们讲述的是用typename T来区分两个模板实例,但是这里的一个指针、一个整形常量(统称非模板型模板参数)就可以直接区分模板实例:
  1. template<int* p>
  2. struct wrapper
  3. {
  4. public:
  5. wrapper(){ id++; }
  6. int get(){ return *p; }
  7. void set(int v){ *p = v; }
  8. public:
  9. static int id;
  10. };
  11. template<int* p> int wrapper<p>::id = ;
  12.  
  13. int global_variable = ;
  14. int global_variable3 = ;
  15.  
  16. int main()
  17. {
  18. wrapper<&global_variable> gwrapper;
  19. printf("ID:%d\n", gwrapper.id);
  20.  
  21. wrapper<&global_variable> gwrapper4;
  22. printf("ID:%d\n", gwrapper4.id);
  23.  
  24. wrapper<&global_variable3> gwrapper3;
  25. printf("ID:%d\n", gwrapper3.id);
  26.  
  27. getchar();
  28.  
  29. }
4、成员函数指针模板参数

  1. class some_value
  2. {
  3. int value;
  4. public:
  5. some_value(int _value) :value(_value){}
  6. int add_by(int op){ return value += op; }
  7. int sub_by(int op){ return value -= op; }
  8. int mul_by(int op){ return value *= op; }
  9. };
  10.  
  11. typedef int (some_value::* some_value_mfp)(int);
  12.  
  13. template<some_value_mfp func>
  14. int call(some_value &value, int op){ return (value.*func)(op); }//*是必要的,否则会认为是在使用value类的成员func
  15.  
  16. void main()
  17. {
  18. some_value v0();
  19. printf("%d\n", call<&some_value::add_by>(v0, ));//&是必要的,否则会认为是调用some_value::add_by但是没给参数
  20. printf("%d\n", call<&some_value::sub_by>(v0, ));
  21. printf("%d\n", call<&some_value::mul_by>(v0, ));
  22. getchar();
  23. }
 
 
5、模板型模板参数
首先我有三个模板:
  1. template<typename T>
  2. struct inc
  3. {
  4. void operator()(T &v) const { ++v; }
  5. };
  6.  
  7. template<typename T>
  8. struct dec
  9. {
  10. void operator()(T &v) const { --v; }
  11. };
  12.  
  13. template<typename T>
  14. struct print
  15. {
  16. void operator()(T &v) const { std::cout << ' ' << v; }
  17. };
 
这三个模板决定了foreach生成不同的实例(当然还有foreach本身的第二个模板参数),这里注意只有类模板可以作为模板参数,所以这里只能用class而不能用struct:
  1. template<template<typename TT> class Func, typename T>
  2. void foreach(T array[], unsigned size)
  3. {
  4. Func<T> func;
  5. for (unsigned i = ; i < size; i++)
  6. {
  7. func(array[i]);
  8. }
  9. }
在foreach中使用第一个模板 or 在foreach中使用第二个模板 or 在foreach中使用第三个模板?都有可能!所以要在foreach中添加一个模板参数用来决定使用哪个模板,这就是模板的模板,也就是模板型模板参数。
  1. void main()
  2. {
  3. int array[] = { , , , , , , };
  4. foreach<print>(array, );
  5. foreach<inc>(array, );
  6. foreach<dec>(array, );
  7.  
  8. getchar();
  9. }
 
 

《深入实践C++模板编程》之三——模板参数类型详解的更多相关文章

  1. VMware 虚拟化编程(2) — 虚拟磁盘文件类型详解

    目录 目录 前文列表 虚拟磁盘文件 VMDK 用户可以创建的虚拟磁盘类型 VixDiskLib 中支持的虚拟磁盘类型 虚拟机文件类型 前文列表 VMware 虚拟化编程(1) - VMDK/VDDK/ ...

  2. VMware 虚拟化编程(7) — VixDiskLib 虚拟磁盘库详解之三

    目录 目录 前文列表 VixDiskLib 虚拟磁盘库 VixDiskLib_GetMetadataKeys VixDiskLib_ReadMetadata 获取虚拟磁盘元数据 VixDiskLib_ ...

  3. VMware 虚拟化编程(5) — VixDiskLib 虚拟磁盘库详解之一

    目录 目录 前文列表 VixDiskLib 虚拟磁盘库 虚拟磁盘数据的传输方式 Transport Methods VixDiskLib_ListTransportModes 枚举支持的传输模式 Vi ...

  4. VMware 虚拟化编程(6) — VixDiskLib 虚拟磁盘库详解之二

    目录 目录 前文列表 VixDiskLib 虚拟磁盘库 VixDiskLib_Open 打开 VMDK File VixDiskLib_Read 读取 VMDK File 数据 VixDiskLib_ ...

  5. 2017.2.9 深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二)-----配置文件详解

    深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二) ------配置文件详解 8.2 MyBatis-Spring应用 8.2.1 概述 本文主要讲述通过注解配置MyBa ...

  6. Scala 深入浅出实战经典 第62讲:Scala中上下文界定内幕中的隐式参数实战详解

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  7. Scala 深入浅出实战经典 第60讲:Scala中隐式参数实战详解以及在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  8. Java多线程编程中Future模式的详解

    Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Futu ...

  9. Oracle GoldenGate中HANDLECOLLISIONS参数使用详解

    Oracle GoldenGate中HANDLECOLLISIONS参数使用详解   HANDLECOLLISIONS 是一个 replicat 进程参数,主要在 initial load 中使用.在 ...

随机推荐

  1. 性能测试 | Linux线程数、进程查询

    I. 最大值查询 1. 最大进程数查询 1 cat /proc/sys/kernel/pid_max 个人阿里云机器(单核2g)上输出如下 1 32768 2. 最大线程数查询 1 cat /proc ...

  2. Android通讯-webSocket

    概述 上一篇简单的认识了Socket以及他的使用,在学习过程中看到了WebSocket的身影,于是乎百度了一把,这货也可以做全双工的网络通讯,而且是html5提出来的新东西!程序员嘛!就是要对新的东西 ...

  3. [转]MySQL查询语句执行过程详解

    Mysql查询语句执行原理 数据库查询语句如何执行?语法分析:首先进行语法分析,对使用sql表示的查询进行语法分析,生成查询语法分析树.语义检查:检查sql中所涉及的对象以及是否在数据库中存在,用户是 ...

  4. Android关闭通知栏后toast无法提示的解决方案

    https://github.com/getActivity/ToastUtils PS:release版本注意加上混淆规则

  5. 301跳转与URL转发有什么区别

    在购买域名时,域名本身是不带有www的,但由于域名要通过DNS服务器解析后才可以使用,在这个过程中每一个域名是会指向一个web服务器ip地址,由于在很早之前网站方都会增加一个"www&quo ...

  6. RabbitMQ学习之:(十)AMQP和RabbitMQ介绍 (转贴+我的评论)

    From: http://www.infoq.com/cn/articles/AMQP-RabbitMQ 准备开始 高级消息队列协议(AMQP1)是一个异步消息传递所使用的应用层协议规范.作为线路层协 ...

  7. Hadoop HDFS命令学习笔记

    cat Usage: hadoop fs -cat [-ignoreCrc] URI [URI ...] Copies source paths to stdout. Options The -ign ...

  8. MATLAB学习(十)实现文件、图像读写

    t=1:5; s1=sin(t); s2=cos(t); s=[t;s1;s2]; fid1=fopen('test.dat','wt'); fprintf(fid1,'\nThis is a For ...

  9. aws S3存储概念

    S3存储(Simple Storage Service) 存储桶:存储桶是S3中用于存储对象的容器.每个对象都存储在一个存储桶中. 对象:对象是S3中存储的基本实体.对象由对象数据和元数据组成.数据部 ...

  10. DevOps - 微服务与Serverless

    微服务 简介 "微服务"强调的是服务的大小,它关注的是某一个点. "微服务架构"则是一种架构思想,需要从整体上对软件系统进行通盘的考虑. 通俗来说,微服务架构就 ...