模板元编程简介

从引入 template 关键字开始,C++里就出现了泛型编程,而又泛型编程衍生出的模板元编程(template meta_programming,简称“元编程”)则是众多编程范式中最复杂、最强大和最具有权威的一种。所谓“元编程”——metaprogramming,有着完全不同于普通程序的许多特点,是一种全新的编程体验。下面将介绍模板元编程的一些基础概念,它们是现代C++和boost程序库组件的基础。

1. 概述

元编程(meta-programming)也被称为“超程序”,“超编程”或“产生式编程”,这样说法一定程度上反映了其本质——它是一种位于普通程序之上、超越普通程序的程序,可以操纵、产生程序的程序。模板元编程本质上是泛型编程的一个子集,从广义上来说,所有使用template 的泛型代码都可以称作元程序——因为泛型编程代码并不是真正可编译执行的代码,它们只是定义了代码的产生规则,是用来生成代码的“模板”。然而模板元编程又不完全等同于泛型编程,它是一种“函数式编程”,是图灵完备的,可以“计算”任何东西。
模板元编程的允许是在编译期,它把编译器变成了元程序的解释器。

2. 语法元素

模板元编程产生的元程序是在编译期执行的程序,操作对象也不是普通的变量,因此不能使用运行时的C++ 关键字(if、else、for),可以的语法元素相当有限,最常用的包括:

  • enum、static 用来定义编译期的变量
  • typedef、using,最重要的元编程关键字,用于定义元数据
  • template,模板元编程的“起点”,主要用于定义元函数
  • “::”,域运算符,用于解析类型作用域获取计算结果(元数据)

3. 元数据

元编程可操作的数据就称为“元数据”(meta date),也就是C++ 编译器在编译期可操作的数据,它是模板元编程的基础。元数据都是不可变的,不能够就地修改,最常见的元数据是整数和C++ 类型(type)。这些元数据不是运行时的普通变量,而是如 int、double、class(非模板类)这样的抽象数据类型。要是对元数据再细分归类,则它又可分为:

  • 整数元数据
  • 值型元数据(int、double等POD值类型)
  • 函数元数据(函数类型)
  • 类元数据(class、struct等用户自定义类型)

对于下面所提到的‘元数据“,特征非整数类型的元数据。
使用tyoedef 关键字可以任意定义(声明)元数据,很像运行时的变量定义,如:

typedef int mtes_data1;   //元数据meta_data1, 值为 int
typedef std::vector<float> meta_data2; //元数据meta_data2,值为vector<float>

使用using 也可以达到同样的效果
using meta_data1 = int;
using meta_data2 = std:;vector<float>;

4. 元函数

元函数(meta function)是模板元编程中用于操作处理元数据的”构件“,可以在编译期被”调用“,因为其功能和形式类似运行时的函数而得名,是元编程里的核心概念。它实际上是一个类或者模板类,通常形式为:

template<typename arg1, typename arg2, ...>  //元函数参数列表
struct meta_function  //元函数名
{
	typedef sone-define type;  //元函数返回元数据
	//using type = some-define; 

	static int const val = some-int;  //元函数返回的整数
};//结束

编写元函数就像是编写一个普通的运行是函数,但形式上却是一个模板类:

  • 函数参数列表的园括号”()“ 变成了模板列表的尖括号”<>“
  • 函数的形参变成了模板参数(即元数据),并且要使用关键字 typedef 修饰
  • 因为不能使用运行时关键字,所以元函数不能像其他普通函数那样使用return 返回计算结果,而是需要在内部使用typedef / using 定义一个名为 type 的类型(元数据)或者名为val 的值作为返回
  • 最后以分号结束,因为它本质上是一个类

元函数也可以没有返回值(即不定义内部类型type),也可以有重载(模板特例/偏特化),也可以有缺省参数,也可以分为无参、单参、多参、可变参等类别。但元函数没有普通函数参数传值、传引用的区别,也没有函数指针的概念。如果有必要,元函数可以使用 typedef / using 关键字 “返回” 任意多个返回值,并且这些没有顺序关系,能够用 ”::“ 来任意获取。为表述方便,下面将只返回 ::type 的元函数称为标准元函数,而返回多个元数据的元函数称为非标准元函数。
下面给一段值元函数的代码:

template<int N, int M>  //两个元数据
struct meta_func
{
	static const int val = N + M; //编译期计算整数之和
};

cout << meta_func<10, 10>::val << endl; //计算结果 20

这里需要主要的是meta_func 的执行过程,它的计算在编译期的时候就已经完成了(即模板实例化),meta_func::val 实际上是一个编译期常量,程序运行时不会有任何计算动作而是直接使用结果。如果这是一个大型的元函数,那么在编译期节约的计算量就会相当可观,可以显著提高程序运行的效率。
由于元函数的计算发生在编译期,所以下列代码不能成立:(不能使用运行时的变量)
在这里插入图片描述
下面示范了另一个元函数,它返回元函数参数列表中的第一个元数据:

template<typename T1, typename T2>  //两个形参
struct select1st
{
	typedef T1 type;  //返回T1,等价于using type = T1
};

5. 元函数转发

元函数转发是模板元编程中一个经常用到的惯用法,相当于运行时的函数转发调用,但在模板元编程中则要用 public 继承实现,模板参数传递给父类完成元函数的 ”调用“,这样的子类会自动获得父类的::type 定义,同时也完成了元函数的返回。例如,下面代码把元函数数据调换位置后,转发给之前定义的元函数 select1st ,相当于select2nd 的功能:

template<typename T1, typename T2>
struct forword: select1st:  //元函数转发,默认是public继承
	select1st<T2, T1>  //参数位置变动
{};


template<typename T1, typename T2>
struct forward  //不用转发
{
	typedef typename select1st<T2, T1>::type type; //调用元函数计算
};

易知,元函数转发因为使用了类继承所以更加简洁

6. 工具宏

模板元编程是一种全新的C++ 编程范式,但仍然使用原有的语法,通篇的 typedef / using 、 template 关键字使元程序不易理解,所有完美可以定义一些工具宏,均以“mp_”开头(或者用增加习惯的方法定义),这样能够便于我们理解。

#define mp_arglist template //元函数参数列表
#define mp_arg    typename  //元函数参数声明
#define mp_function struct //元函数定义
#define mp_data  typedef //元数据定义

#define mp_return(T) mp_data T type  //元函数返回
//using type = T
#define mp_exec(Func)  Func::type   //获取元函数返回结果
#define eval(Func)   Func::value   //获取元函数返回值

这些分别把 template 、typename、struct 和 typedef 这四个模板元编程中最常用的关键字进行了重命名。

  • mp_arglist 表示元函数的参数列表开始
  • mp_arg 表示元函数的参数
  • mp_function 表示定义一个元函数
  • mp_data 表示定义一个元数据
  • mp_return / mp_eval / mp_exec 定义了元编程中约定返回值用法,较原写法更清楚
mp_data int meta_data1;   //元数据meta_data1,值为int

mp_arglist<mp_arg T1, mp_arg T2>    //元函数的参数是T1、T2
mp_function select1st  //元函数select1st
{
	mp_return(T1);  //返回T1  -> type
}

很明显,使用工具宏使元程序看起来更加清楚,易于区分。但是由于宏预处理机制自身的“缺陷”,后三个工具宏的作用有限,它们只能处理简单的参数,如果带有 “,”,那么模板类就会失效,但Boost 库里面的 BOOST_IDENTITY_TYPE 来解决。

7. 应用示例

下面通过两个例子来示范元编程的基本使用:

  • 编译期比较大小
mp_arglist<int L, int R>
mp_function static_min  //元函数 static_min
{
	static const int value = (L < R) ? L : R;
};

assert((static_min<10, 20>::value == 10)); //编译期比较
  • demo_func 输入元数据 T 是指针类型返回const T,否则 const T*
mp_arglist<mp_arg T>   //单参元函数
mp_function demo_func
{
	mp_return(const T*);   //通常情况返回const T*
};

mp_arglist<mp_arg T>
mp_function demo_func<T*>   //对T*情况进行模板实例化
{
	mp_return(const T);
};

//这里用is_same元函数进行验证 #include<boost/type_traits/is_same>
assert((is_same<mp_exec(demo_func<int>), const int*>::value));
assert((is_same<mp_exec(demo_func<int*>), const int>::value));
//这里的 assert 必须用两对括号来包围断言

8. 总结

我们介绍了模板元编程的基础知识,包括元编程 / 元程序 / 元数据 和 元函数转发等概念。

  • 元编程是一种超越普通程序的程序,在C++中元编程是使用模板技术实现的,所以它右被称为模板元编程。元程序可以由C++编译期解释执行,把部分计算量由运行时转移到编译时完成,提高程序运行效率,但元编程更大的用途是类型推导,操作C++类型体系。
  • 元数据是元编程的操作对象,可以是整数(含bool)或任意的C++类型
  • 元函数是元编程的核心,它表现为C++的一个模板类,我们必须使用元函数才能操作元数据。它以内部定义::type 或 ::value 返回计算结果,并可以使用public 继承的方式实现元函数转发

至此结束

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/299706.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记——C++ 逢七过 问题

试用for循环语句解决以下案例&#xff1a; 案例描述&#xff1a;从1开始数到数字100&#xff0c;如果数字个位含有7&#xff0c;或者数字十位含有7&#xff0c;或者该数字是7的倍数&#xff0c;我们打印敲桌子&#xff0c;过&#xff0c;其余数字直接打印输出。 思路&#xf…

C++进阶--map和set的介绍及使用

map和set的介绍及使用 一、关联式容器与键值对关联式容器键值对pair树形结构的关联式容器 二、set2.1 set的介绍2.2 set的使用2.2.1 set的模板参数列表2.2.2 set的构造2.2.3 set的迭代器2.2.4 set的容量2.2.5 set修改操作2.2.6 set的使用举例 三、multiset3.1 multiset的介绍3.…

MySQL——用户管理

目录 一.用户管理 二.用户 1.用户信息 2.创建用户 3.删除用户 4. 修改用户密码 三.数据库的权限 1.给用户授权 2.回收权限 一.用户管理 如果我们只能使用root用户&#xff0c;root的权限非常大,这样存在安全隐患。这时&#xff0c;就需要使用MySQL的用户管理&#xff…

强化学习5——动态规划在强化学习中的应用

动态规划在强化学习中的应用 基于动态规划的算法优良 &#xff1a;策略迭代和价值迭代。 策略迭代分为策略评估和策略提升&#xff0c;使用贝尔曼期望方程得到一个策略的状态价值函数&#xff1b;价值迭代直接使用贝尔曼最优方程进行动态规划&#xff0c;得到最终的最优状态价…

windows下载官方正版notepad++

一、前言 notepad是一款非常好用的编辑器&#xff0c;简洁、快速、高效。可是很多时候我们想去官网下载时&#xff0c;百度出来的都是一堆第三方下载地址&#xff0c;捆绑流氓软件&#xff0c;要么就是付费&#xff0c;作为一款优秀开源软件&#xff0c;我们必须要知道正确的下…

蓝桥杯练习题(一)

&#x1f4d1;前言 本文主要是【算法】——蓝桥杯练习题&#xff08;一&#xff09;的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 …

物联网产品中,终端、网关、协议、PaaS、SaaS之间的关系

在互联网产品中&#xff0c;经常提到的终端、网关、协议、PaaS、SaaS之间&#xff0c;到底有什么关系呢&#xff1f; 一、基本概念 在百度/其他地方搜集的信息中&#xff0c;对于终端、网关、协议、PaaS、SaaS的解释各有不同&#xff0c;整理如下&#xff1a; 终端&#xff1…

AQS 抽象队列同步器

AQS AQS &#xff08;抽象队列同步器&#xff09;&#xff1a; AbstractQueuedSynchronizer 是什么 来自jdk1.5&#xff0c;是用来实现锁或者其他同步器组件的公共基础部分的抽象实现&#xff0c;是重量级基础框架以及JUC的基石&#xff0c;主要用于解决锁分配给谁的问题整体…

5年经验之谈 —— 探索自动化测试用例设计粒度!

自动化测试用例的粒度指的是测试用例的细致程度&#xff0c;即每个测试用例检查的功能点的数量和范围。 通常&#xff0c;根据测试用例的粒度&#xff0c;可以被分为3种不同的层次&#xff0c;从更低层次的细粒度到更高层次的粗粒度。 第一种&#xff1a;单元测试 - 细粒度 单…

编译ZLMediaKit(win10+msvc2019_x64)

前言 因工作需要&#xff0c;需要ZLMediaKit&#xff0c;为方便抓包分析&#xff0c;最好在windows系统上测试&#xff0c;但使用自己编译的第三方库一直出问题&#xff0c;无法编译通过。本文档记录下win10上的编译过程&#xff0c;供有需要的小伙伴使用 一、需要安装的软件…

2024年【化工自动化控制仪表】报名考试及化工自动化控制仪表考试技巧

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 化工自动化控制仪表报名考试是安全生产模拟考试一点通生成的&#xff0c;化工自动化控制仪表证模拟考试题库是根据化工自动化控制仪表最新版教材汇编出化工自动化控制仪表仿真模拟考试。2024年【化工自动化控制仪表】…

outlook邮件群发单显技巧?群发怎么单显?

outlook邮件群发单显如何设置&#xff1f;QQ邮箱怎么群发单显&#xff1f; 在群发邮件时&#xff0c;如何让每个收件人只看到自己的名字&#xff0c;而不是其他人的名字&#xff0c;这就涉及到所谓的“单显”技巧。下面蜂邮EDM就为大家揭秘Outlook邮件群发单显的奥秘。 outlo…

DBSCAN聚类模型

目录 介绍&#xff1a; 一、数据 二、建模 三、评价指标 3.1metrics.homogeneity_score 3.2metrics.completeness_score 3.3metrics.v_measure_score 3.4metrics.adjusted_rand_score 3.5metrics.adjusted_mutual_info_score 3.6metrics.silhouette_score 四、画图…

聊天Demo

文章目录 参考链接使用前端界面消息窗口平滑滚动至底部vue使用watch监听vuex中的变量变化 参考链接 vue.js实现带表情评论功能前后端实现&#xff08;仿B站评论&#xff09; vue.js实现带表情评论仿bilibili&#xff08;滚动加载效果&#xff09; vue.js支持表情输入 vue.js表…

【Java】2023年Java语言盘点

2023年Java语言盘点 前言语言排行榜JDK 各个版本使用情况主流框架支持情况JDK 21其他项目参考 前言 星河流转&#xff0c;日月更替&#xff0c;在2023这年里&#xff0c;Java也迎来了它的第28个年头。在这一年里&#xff0c;Java生态系统发生了许多让人瞩目的变化&#xff0c;…

计算机Java项目|Springboot疫情网课管理系统

项目编号&#xff1a;L-BS-ZXBS-07 一&#xff0c;环境介绍 语言环境&#xff1a;Java: jdk1.8 数据库&#xff1a;Mysql: mysql5.7 应用服务器&#xff1a;Tomcat: tomcat8.5.31 开发工具&#xff1a;IDEA或eclipse 二&#xff0c;项目简介 疫情网课也都将通过计算机…

智能计价器Scratch-第14届蓝桥杯Scratch省赛真题第5题

5. 智能计价器&#xff08;80分&#xff09; 背景信息&#xff1a;A城市的出租车计价&#xff1a;3公里以内13元&#xff0c;基本单价每公里2.3元(超过3公里的部分&#xff0c;不满1公里按照1公里收费&#xff09;&#xff0c;燃油附加费每运次1元。例如&#xff1a;3.2公里的…

1875_如何提升开关应用中的EMI表现

Grey 全部学习内容汇总&#xff1a; https://github.com/GreyZhang/g_hardware_basic 1875_如何提升开关应用中的EMI表现 看了一份ST的应用笔记&#xff0c;简单了解了一下EMI相关的一些设计&#xff0c;感觉还比价有收获。整理一下自己的收获点。 资料整理过程说明 参考资…

并发(8)

目录 46.Thread.sleep(),Object.wait(),Condition.await(),LockSupport.part()的区别&#xff1f; 47.如果在wait&#xff08;&#xff09;之前执行了notify&#xff08;&#xff09;会怎样&#xff1f; 48.如果在park()之前执行了unpark()会怎样&#xff1f; 49.什么是AQS&…

【Spring Cloud】关于Nacos配置管理的详解介绍

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Spring Cloud》。&#x1f3af;&#x1f3af; &am…