非类型模板参数 和 模板型模板参数
整数以及枚举类型;指向对象或者函数的指针;对对象或函数的引用;指向对象成员的指针。统称为非类型模板参数。
模板型模板参数,是指模板参数还可以是一个模板。
 
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. 转载 AMI方案和Insyde方案

    闲来无事回头看看,自毕业以来一直都在BIOS这个行业打转,目前的各种新闻来看全球的IT行业都似乎不太景气,PC出货量一泻千里,想要有所转机看起来不是一天两天的事情,想来无事来看看做过的几个UEFI B ...

  2. mac 配置apache

    Apache配置 对httpd.conf文件的配置.首先打开 /etc/apache2/httpd.conf文件,也就是Apache2.4的相关配置文件. 连接php 首先我们将相应的PHP版本配置进 ...

  3. <javaScript>谈谈JavaScript中的变量、指针和引用

    1.变量我们可能产生这样一个疑问:编程语言中的变量到底是什么意思呢?事实上,当我们定义了一个变量a时,就是在存储器中指定了一组存储单元,并将这组存储单元命名为a.变量a的值实际上描述的是这组存储单元中 ...

  4. 最新create-react-native-app搭建rn教程

    一.前置条件: 1.nodeJS环境 2.npm 3.yarn 二.安装及项目初始化 1.安装脚手架 npm install -g create-react-native-app 2.用脚手架初始化创 ...

  5. Flask中的请求上下文和应用上下文

    在Flask中处理请求时,应用会生成一个“请求上下文”对象.整个请求的处理过程,都会在这个上下文对象中进行.这保证了请求的处理过程不被干扰.处理请求的具体代码如下: def wsgi_app(self ...

  6. 深度学习之强化学习Q-Learning

    1.知识点 """ 1.强化学习:学习系统没有像很多其他形式的机器学习方法一样被告知应该做什么行为, 必须在尝试之后才能发现哪些行为会导致奖励的最大化,当前的行为可能不仅 ...

  7. MySQLdb User's Guide

    MySQLdb MySQLdb-1.2.2 API documentation http://mysql-python.sourceforge.net/MySQLdb-1.2.2/ MySQLdb U ...

  8. Introduction to statistical learning:with Applications in R (书,数据,R代码,链接)

    http://faculty.marshall.usc.edu/gareth-james/ http://faculty.marshall.usc.edu/gareth-james/ISL/

  9. Swagger 介绍

    简介Swagger 是最流行的 API 开发工具,它遵循 OpenAPI Specification(OpenAPI 规范,也简称 OAS).Swagger 可以贯穿于整个 API 生态,如 API ...

  10. Google 搜索语法

    1. 逻辑与/或:AND/OR注意 AND.OR 必须大写OR 指令优先于 AND 指令AND 指令一般以space或+代替OR 指令可用 | 代替2. 逻辑非:-3. 完整匹配:" &qu ...