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

template<typename T, unsigned Size>
class CArray2
{
public:
CArray2()
{
id++;
}
~CArray2(){} T elems[Size];
public:
T& operator[](unsigned i) throw (std::out_of_range)
{
if (i >= size)
{
throw std::out_of_range("Access out of range\n");
}
else
{
return elems[i];
}
} public:
static int id;
}; template<typename T, unsigned Size> int CArray2<T, Size>::id = ; //顺便我们也应该了解这种带有非类型模板参数的模板类如何定义一个static成员 void main()
{
CArray2<char, > array0;
printf("ID:%d\n", array0.id); CArray2<char, > array1;
printf("ID:%d\n", array1.id); CArray2<int, > array3;
printf("ID:%d\n", array3.id); CArray2<int, > array4;
printf("ID:%d\n", array4.id); getchar();
}
运行结果如下:

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

 template<typename T, void(*f)(T &v)>
void foreach(T array[], unsigned size)
{
for (unsigned i = ; i < size; ++i)
{
f(array[i]);
}
} template<typename T>
void inc(T &v){ ++v; } template<typename T>
void dec(T &v){ --v; } template<typename T>
void print(T &v){ printf("%d ", v); } void main()
{
int array[] = { , , , , , , , };
foreach<int, print<int>>(array, ); foreach<int, inc<int>>(array, ); getchar();
}
 
3、指针及引用模板参数
只有指向全局变量及外部变量及类静态变量的指针及引用才可以作为模板参数。函数的局部变量、类成员变量等均不能作为模板参数。因为模板参数值必须是编译时已知的。

 template<int* p>
struct wrapper
{
int get(){ return *p; }
void set(int v){ *p = v; } }; template<int &p>
struct wrapper2
{
int get(){ return p; }
void set(int v){ p = v; }
}; int global_variable = ; int main()
{
wrapper<&global_variable> gwrapper;
wrapper2<global_variable> gwrapper2;
}
有一个明确的结论是,global_variable 决定了gwrapper的类型。即如果我添加一个
int global_variable3 = 0; 和 wrapper<global_variable3> gwrapper3;
那么gwrapper和gwrapper3并非同一个类型。
之前我们讲述的是用typename T来区分两个模板实例,但是这里的一个指针、一个整形常量(统称非模板型模板参数)就可以直接区分模板实例:
 template<int* p>
struct wrapper
{
public:
wrapper(){ id++; }
int get(){ return *p; }
void set(int v){ *p = v; }
public:
static int id;
};
template<int* p> int wrapper<p>::id = ; int global_variable = ;
int global_variable3 = ; int main()
{
wrapper<&global_variable> gwrapper;
printf("ID:%d\n", gwrapper.id); wrapper<&global_variable> gwrapper4;
printf("ID:%d\n", gwrapper4.id); wrapper<&global_variable3> gwrapper3;
printf("ID:%d\n", gwrapper3.id); getchar(); }
4、成员函数指针模板参数

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

《深入实践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. JS高级_变量提升和函数提升

    先执行变量提升,后执行函数提升 function a(){} var a console.log(typeof a)//function

  2. OpenCL如何获取最小线程并行粒度

    由于OpenCL是为各类处理器设备而打造的开发标准的计算语言.因此跟CUDA不太一样的是,其对设备特征查询的项更上层,而没有提供一些更为底层的特征查询.比如,你用OpenCL的设备查询API只能获取最 ...

  3. 用C#做成的程序如何打包部署,在其他PC机运行

    刚刚接触C#,在linux下面做习惯了c和c++,在本机运行OK后,得瑟的去别的机子,居然运行不了,你妹啊,怎么回事,没有.NET Framework,原来和java类似,.NET Framework ...

  4. Swift小知识点总结

    1>判断输入字符串位数: if phoneTextField.text?.lengthOfBytes(using: .utf8) != 11 { self.showHint(hint: &quo ...

  5. springboot和jsp,以及引入jquery

    包: src: main: java: resources: static: templates: test: 2.在static导入jquery源文件,在templates写入jsp文件 3.配置文 ...

  6. JS GZIP压缩

    GZIP压缩,GZIP解压需要用到 pako.js 文件:下载地址:https://download.csdn.net/download/qq_35713752/10627338 使用方法: JS压缩 ...

  7. 小程序接入云通信IM

    小程序接入云通信IM--插件 小程序微信后台搜索AI情报官组件即可获得小程序云通信IM的即时通信能力

  8. windows端口映射

    1. 查看netsh interface portproxy show all 2. 添加端口映射转发netsh interface portproxy add v4tov4 listenaddres ...

  9. ASP.NET Core 入门笔记8,ASP.NET Core MVC 分部视图入门

    一.前言 1.本教程主要内容 ASP.NET Core MVC (Razor)分部视图简介 ASP.NET Core MVC (Razor)分部视图基础教程 ASP.NET Core MVC (Raz ...

  10. Windows To Go 制作详解

    拥有 Mac 的同学大概都会碰到一个头疼的问题,那就是使用 Windows 的使用需求.macOS 虽好,不过总是会有一些讨厌的软件没有 Mac 版本,这时就不得不在 Mac 上跑 Windows 了 ...