【C++ Metaprogramming】0. 在C++中实现类似C#的泛型类

news2024/11/15 17:52:22

两年前,笔者因为项目原因刚开始接触C++,当时就在想,如果C++有类似C#中的泛型限定就好了,能让代码简单许多。我也一度认为:

虽然C++有模板类,但是却没办法实现C#中泛型特有的 where 关键词:

public class Parent { /* ... */ }
public class Child : Parent { /* ... */ }
public class AnotherClass { /* ... */ }

public class GenericClass<T> where T : Parent 
{
	public static void DoSomething(T item) { }
}

static void Main(string[] args)
{
	Child c = new Child();
	AnotherClass a = new AnotherClass();
	GenericClass<Parent>.DoSomething(c);
	GenericClass<Child>.DoSomething(c);
	GenericClass<AnotherClass>.DoSomething(); // --- 报错
}

但实际上,C++通过不仅能做到这部分内容,而且比C#能做的还要更多。而且很多内容甚至是可以在Compile Time就能做到限制的 —— 不用等到代码跑起来,编译的时候就能够告诉你代码哪里有问题。

C++实现这部分功能所涉及到的技术就是 Metaprogramming,直译过来是“元编程”。

我相信 元编程 这个名字对于大多数人来说是没有意义的,就好像我们第一次听到某种深海动物名字一样,它叫什么不重要,重要的是,它是什么。

比如我现在向大家介绍一种深海动物,“须蛸”。如果我不放这个图,“须蛸”这个名字就是没有任何意义的 (放了这图似乎也没多大意义,但是至少你更加信服这个名字不是我瞎编的了)。
在这里插入图片描述
所以什么是元编程?


元编程是对代码的编程


元编程英文里的Meta可以认为是“超级”的意思,这里的“超级”应该不是指“能力强过、高过”,亦不是说“凌驾于xxx之上”的意思。

在我个人理解里,Metaprogramming技术本身并非高明于我们平时的普通编程技术,它只是思维方式不一样。但不幸的是,C++的Metaprogramming是一只披着C++语言外皮的“狼”(需要完全用另一套思维逻辑去思考/实现的另一个编程领域),所以笔者认为这也是为什么许多C++用了许多年的人谈到Metaprogramming这个领域时仍噤若寒蝉——因为它们压根不是一个东西,只是恰好语法底层用到的是同一套东西而已——而且在实际工作中你可以完全不使用该内容而写好自己的代码(只不过在某些情况可能要多付出一点体力劳动……不过有Vim在,那也不是什么大问题嘛)。

说到Meta的“超级”的含义,Metaprogramming在我这里十分像优化领域里的Hyperparameter Optimization (超参数优化)里的“Hyper” —— 同样是“超级”的意思,同样的,超参数优化也不是说它本身相较于普通的参数优化有什么更深奥的技术,只不过是Optimization的参数的Optimization,优化参数的优化,所以就用差不多的技术在对不同的目标套了一层。

在这里插入图片描述
巧了么不是,Metaprogramming是对普通programming的programming,我觉得起名叫Hyperprogramming也是挺不错的。而且他俩都有一个共同点,那就是特别烧脑,都要在原有的概念上嵌套一层。

好了,说了这么多,那倒底啥是Metaprogramming?下面就举个简单的例子,我们用C#来实现它,简单说说啥是Metaprogramming。


什么是Metaprogramming


现在我们要写个程序,需求是:

能够返回正整数1-5的平方。

普通编程的方法是:

int ReturnSquare(int i) => i * i;

Metaprogramming的方法是:
写一个Console程序,输出一个cs文件:

static void Main(string[] args)
{
	using var fs = File.Create("d:\square.cs", FileMode.Create);
	using var sr = new StreamWriter(fs);
	sr.AppendLine("int ReturnSquare(int i) => i * i");
	sr.Flush();
}

这就是Metaprogramming。


???


是的,这就是Metaprogramming。


不过这只是最广义上的概念,但这个概念的确如此 —— 用代码去写代码。这个技术就是Metaprogramming。

我们一般说C++的Metaprogramming是指 “使用C++的模版技术来借助编译器在编译时帮我们生成、检查代码”

笔者这里再插一段,C#也是有模版的,也是需要借助编译器才可以,感兴趣的读者可以自行搜索“C# Text Template”,或者官方链接:
https://learn.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2022

对于Metaprogarmming的介绍今天就先到这里了,后面有想到的会继续补充,下面的时间留给标题里的内容。

我是被标题吸引的,我要C++实现泛型限定

  1. 限定Parent
#include <type_traits>

class Parent{};
class Child : public Parent{};
class AnotherClass {};

namespace hidden {
	template <bool b, typename T>
	struct Helper
	{
		static void DoSomething(T item) = delete;
	};
	template <typename T>
	struct Helper<true, T>
	{
		static void DoSomething(T item)
		{
			// ...
		}
	};
}

template <typename T>
using Generic = hidden::Helper<std::is_base_of_v<Parent, T>, T>;

int main()
{
	Child c;
	AnotherClass a;
	Generic<Parent>::DoSomething(c);
	Generic<AnotherClass>::DoSomething(a); // -- 报错
}
  1. 限定 “类里必须要有Foo成员函数” 才可使用

// 具备成员函数Foo
class HasFoo
{
public:
	int Foo();
};

// 不具备成员函数Foo,仅有静态函数Foo
class NoFoo 
{
public:
    static int Foo(); 
};

namespace hidden {
    template <typename T>
    std::true_type Call(std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::Foo)>, void*>);
    template <typename T>
    std::false_type Call(...);
};

template <typename T>
using has_member_function_foo = decltype(hidden::Call<T>(nullptr));

int main()
{
    static_assert( // --- passed
        has_member_function_foo<HasFoo>::value,
        "does not have a member of Foo");

    static_assert( // --- failed 编译报错
        has_member_function_foo<NoFoo>::value,
        "does not have a member of Foo");
}

好了我们下次再见。🦀

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android 13 wificond讲解

wificond介绍 看如下图,可以知道wificond 进程,该进程位于 system/connectivity/wificond 中。wificond 进程通过标准的 nl80211 命令与 Wi-Fi 驱动程序进行通信。 查看手机也能看到wificond 进程 wificond启动 1. 开机的时候通过调用wificond.rc文件启动wificond system…

docker 部署LNMP

准备工作。 #首先获取nginx配置文件 [rootlocalhost ~]# docker pull nginx:1.23.3 [rootlocalhost ~]# docker run --name nginx --restartalways -d -p 80:80 nginx:1.23.3 [rootlocalhost ~]# mkdir -p /usr/local/nginx/{conf,log,html,conf.d} #复制配置文件 [rootlocalh…

易观千帆 | 2023年3月证券APP月活跃用户规模盘点

易观&#xff1a;2023年3月证券服务应用活跃人数14131.58万人&#xff0c;相较上月&#xff0c;环比增长0.61%&#xff0c;同比增长0.60%&#xff1b;2023年3月自营类证券服务应用Top10 活跃人数6221.44万人&#xff0c;环比增长0.08%&#xff1b;2023年3月第三方证券服务应用T…

使用Spring的五大类注解读取和存储Bean

目录 1.存储Bean对象的注解 1.1 五大类注解 1.2 方法注解 1.3添加注解的依赖 2.注解的使用 2.1 controller注解 2. 2Service注解 2.3.Resopsitory注解 2.4Component注解 2.5Configuration注解 2.6 注解之间的关系 3.方法注解 3.1 方法注解要配合类注解来使用。 3.2…

【Python】ddddoc进行OCR识别和目标检测 ——识别验证码和滑块(安装部署+测试代码注释详细)

目录 安装部署gitee已经上传完整项目requiremen.txt插件作者更新地址 项目结构第一部分 OCR识别部分第二部分 目标检测部分总结 欢迎关注 『Python』 系列&#xff0c;持续更新中 欢迎关注 『Python』 系列&#xff0c;持续更新中 安装部署 gitee已经上传完整项目 requiremen.…

图片对象列表查询与展示(vue+springboot+elementUI)

本文描述场景为 展示&#xff1a;后端从数据库中查询图片对象列表&#xff0c;返回前端展示 多图片展示 先看一下后端表实体 import com.zpmc.common.domain.BaseEntity; import io.swagger.annotations.ApiModel; import lombok.*;import javax.persistence.*; import java.…

8086汇编之乘法指令MUL

2023年4月22日&#xff0c;周六晚上。 今天写汇编作业的时候&#xff0c;碰到了MUL指令&#xff0c;于是把学习到的MUL指令知识记录下来&#xff0c;也可以通过写这篇博客彻底理清MUL指令。 当被乘数和乘数都是8位时&#xff1a; 怎么判断被乘数和乘数是不是8位的&#xff1f…

操作系统之认识进程

目录 什么是进程 进程的状态和转换 进程控制 进程通信 什么是进程 在电脑的任务管理器中&#xff0c;能看到电脑当前运行着的所有进程 那到底什么是进程呢&#xff1f;和我们所看所写的那些程序有什么区别&#xff1f; 操作系统是如何区分这些进程的呢&#xff1f; 那就需要…

银河麒麟(桌面版和服务器版)之远程桌面安装

一、前言 在信创方案中经常介绍支持麒麟系统&#xff0c;实际上麒麟分为银河麒麟和中标麒麟&#xff0c;银河麒麟又分为服务器版和桌面版&#xff0c;服务器器版一般用于应用系统部署&#xff0c;桌面版一般用于日常办公。银河麒麟操作系统作为国产操作系统&#xff0c;是目前国…

java定时任务schedule

在 Java中&#xff0c;可以使用定时任务&#xff08;schedule&#xff09;来实现定时任务。这种定时任务能够根据用户的需求进行时间的控制&#xff0c;让用户可以自由设定每一个任务的开始时间和结束时间。 下面来介绍如何使用 java中的定时任务来实现定时任务。 首先需要在配…

# 将pg日志导入pg数据库的几种方法

将pg日志导入pg数据库的几种方法 文章目录 将pg日志导入pg数据库的几种方法1 方法1&#xff1a;官方方法&#xff08;fiel_fdw&#xff09;1.1 创建file_fdw数据源&#xff08;在此之前需要安装pg的file_fdw扩展&#xff09;1.2 创建外部表并导入数据 2 方法2&#xff1a;用csv…

会议论文与期刊论文的写作差异

AI领域的会议论文和期刊论文在撰写方法上存在一定的差异&#xff0c;读者需要理解这些差异&#xff0c;才能做到有的放矢&#xff0c;提高论文的命中率。如果按照会议论文的风格来写期刊论文&#xff0c;或者按照期刊论文的风格来写会议论文&#xff0c;论文命中的概率将大大降…

服务(第十三篇)lvs(负载均衡器)+nginx(反向代理)+tomcat(后端服务器)+nfs共享

准备6台机器&#xff1a; 192.168.169.10 lvs负载均衡器 192.168.169.20、192.168.169.30 nginx反向代理 192.168.169.40&#xff08;tomcat多实例&#xff09;192.168.169.50&#xff08;tomcat&#xff09; 19.168.169.60&#xff08;nfs共享&#xff09; 实验没啥好说的…

新建项目提交到git指定仓库

新建的项目如何上传到git远程仓库&#xff1a; 1&#xff1a;首先进入需要上传的文件夹&#xff0c;鼠标右键点击Git Bash Here 2&#xff1a;输入git init&#xff0c;初始化git相关配置文件 git init3&#xff1a;输入git remote add origin 你的远程仓库地址&#xff0c;…

收废品小程序的推广策略与实践

随着互联网和移动设备的普及&#xff0c;各种小程序逐渐成为了人们生活中不可或缺的一部分。在这样的背景下&#xff0c;开发一个收废品小程序是一个具有巨大潜力的创意。本文将介绍如何开发一个收废品小程序&#xff0c;并分享一些推广策略和实践经验。 开发一个收废品小程序…

推荐好用的数据库软件sql studio

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言免费功能强大支持数据导入和导出和navicat功能相同 一、sql studio是什么&#xff1f;二、使用步骤1.下载软件2. window版本3.linux 版本配置方式3.如何使用sql…

论文浅尝 | 基于交互模态融合的多模态知识图谱补全

笔记整理&#xff1a;张溢驰&#xff0c;浙江大学硕士&#xff0c;研究方向为多模态知识图谱 链接&#xff1a;https://arxiv.org/abs/2303.10816 动机 多模态知识图谱补全需要将多种模态的信息&#xff08;如图像和文本&#xff09;融入到实体的结构表示中&#xff0c;以此来实…

OpenAI ChatGPT 能取代多少程序员的工作?导致失业吗?

阅读原文&#xff1a;https://bysocket.com/openai-chatgpt-vs-developer/ ChatGPT 能取代多少程序员的工作&#xff1f;导致我们程序员失业吗&#xff1f;这是一个很好的话题&#xff0c;我这里分享下&#xff1a; 一、ChatGPT 是什么&#xff1f;有什么作用 ChatGPT是一种…

操作系统课堂笔记

第一章概述 操作系统&#xff08;Operating System, OS&#xff09;是计算机系统中最重要的系统软件&#xff0c;它统一管理计算机系统的硬件资源与信息资源&#xff0c;控制与调度上层软件的执行并为其提供易于使用的接口。 1.1计算机系统 操作系统在计算机系统中的地位&…

Java学习-MySQL-索引

Java学习-MySQL-索引 索引分类 索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构。 1.主键索引&#xff08;Primary Key&#xff09;&#xff0c;唯一标识&#xff0c;不可重复&#xff0c;并且只能有一个字段可以作为主键。 2.唯一索引&#xff08;Uniq…