CPP Con 2020:Type Traits I

先谈谈Meta Programming

啥是元编程呢?很简单,就是那些将其他程序当作数据来进行处理和传递的编程(私人感觉有点类似于函数式?)这个其他程序可以是自己也可以是其他程序。元编程可以发生在编译时也可以发生在运行时。

还是有点懵?实际上你就认为元编程是一个生成代码的编程技术就好。(StackOverflow:What exactly is metaprogramming? - Stack Overflow, MetaPogramming is just a "Writing code that write code", "Code that generate code")

元函数

我们先回忆一下啥是函数吧:函数是一个子程序,可以接受若干输入,返回输出。

void a_simple_function();
int another_echo(int retWhat, const char* descriptions){using std::cout, std::endl;cout << descriptions << endl;retun retWhat;
}

一般的函数跟外界交互的正常方式是使用一个return返回值:

int echo_value(int val){return val;}

或者是传递一个引用,或指针,这也是一种方式,不过不被主流代码规范所推崇(太混乱)

#define OUT
​
void
getValueByRef(OUT int& gettee)
{gettee = 42;
}
​
void 
getValueByPointer(OUT int* gettee)
{if( is_invalid_pointer(gettee) )throw MY_PTR_EXCEPTION;*gettee = 42;
}

编译器将会检查返回是否在语法层面上合法:举个例子就是你声明返回A,就不可以返回B。保证函数的签名和实际的行为一致。

那元函数呢?实际上元函数不是一个函数!哈哈,他是一个类或者struct!他是不被语法层面上所约束的,另一个意思就是:不存在原生的支持!要自己手撮,我们需要规范来约束自己元编程的规范。

在C++里,元编程是使用模板完成的,一个带有0个或者是多个模板参数的类是我们的元编程的核心!他的行为跟函数很类似:都是接受若干输入,返回一个输出!

看一个例子就好:

struct Offer114514{static constexpr int value = 114514;
};

有点奇怪?哈哈,这样用:

#include <iostream>
​
using std::cout, std::endl;
​
int get114514(){return 114514;
}
​
struct Get114514{static constexpr int value = 114514;
};
​
int main()
{cout << "We can get value by triditional functions > " << get114514() << endl;cout << "Else using meta functions:> " << Get114514::value << endl;
}

只是这样就没意思了。我们继续使用更加高级的,函数做不到的:

#include <iostream>
#include <ostream>
void displayTypeInfo(const char* type){std::cout << "We shell get the type:> " <<type << std::endl;
}
​
template<typename EchoType>
struct Echo{using type = EchoType;
};
​
int main()
{typename Echo<int>::type value = 114514;displayTypeInfo(typeid(value).name());  
}

确实有点多此一举这个例子,但是这里展现出来元函数的一个重要特点,他可以完成类型操作,这里就是存储了一个type信息,我们之后到哪里都可以传递他进入其他函数内部使用!

再看一些例子!

Square Storage

#include <iostream>
template <int value_stored>
struct StaticValueStorage{static constexpr int square(const int val){return val* val;}static constexpr int value = square(value_stored);
};
​
int main()
{static_assert(4 == StaticValueStorage<2>::value);std::cout << "Welp! nothing wrong then!";
}

推介使用C++17以上编译,否则不会通过。

Identifiers

template<auto val>
struct Identity
{using type = decltype(val);static constexpr auto value = val;
};
​
struct YepMe{int age;int id;
public:constexpr YepMe(int a, int id):age(a), id(id){};friend constexpr bool operator==(const YepMe& p1, const YepMe& p2){return p1.age == p2.age && p1.id == p2.id;}
};
​
​
int main()
{static_assert(114514 == Identity<114514>::value);constexpr YepMe me{1, 114514};static_assert(YepMe(1, 114514) == Identity<me>::value);
}

啊哈,关于这种非参数模板不是我们的重点,我们已经看到这里的元编程特性可以展现更多的事情

Type MetaFunctions

我们看到存在这种”奇怪“的技术:

template<typename T>
struct StorageType{using type = T;
}
​
int main()
{StorageType<int>::type value = 1; // questions: is this really valid in C++?
}

可以完成这样表达的,不过,为了更好的表意,当我们先要明确我们表达的就是类型,我们完全可以加上关键字:

typename

我们这样就可以操作类型了

一些技巧

查看标准库,我们时常会使用value或者是type,标准库提供了一种方便的简写:

对于那些值的元函数,我们会使用_v结尾,

对于那些类型的元函数,我们会以_t作为结尾

例子如下:

template<auto val>
struct ValueHolder
{using type = decltype(val);static constexpr auto value = val;
};
​
template<auto val>
inline constexpr auto ValueHolder_v = ValueHolder<val>::value;
​
template<auto val>
using ValueHolder_t = typename ValueHolder<val>::type;
​
​
int main()
{static_assert( 114 == ValueHolder_v<114> );static_assert( std::is_same_v<int, ValueHolder_t<42>> );
}
​

这些约定可以简化我们的调用,真的没人喜欢看又臭又长的模板

一些有用的metafunction

std::integral_constant

template<typename T, T v>
struct integral_constant
{using value_type    = T;using type          = integral_constant<T, v>;constexpr operator value_type() const noexcept{return value;   // 这里是类型转换}constexpr value_type operator()() const noexcept{return value;   // 这里是调用法转换}
};

干嘛的呢?封装常量的。我们下面就开始有了type_traits的一个根基:bool_constant

template<bool B>
using bool_constant = integral_constant<bool, B>;
​
using true_type = bool_constant<true>; // If confused, consider as true
using false_type = bool_constant<false>;

好了!现在我们的true和false就成为了一个元函数,访问是不是真的只需要访问值就行。

C++17 type_traits

一元type_traits

约束如下:

  1. 类模板

  2. 一元参数模板

  3. 可以默认和拷贝构造

  4. 必须从std::integral_constant那里public继承!基类成员不应该被隐藏

二元type_traits

约束如下:

  1. 类模板

  2. 二元参数模板

  3. 可以默认和拷贝构造

  4. 必须从std::integral_constant那里public继承!基类成员不应该被隐藏

变换type_traits

约束如下:

  1. 类模板

  2. 一元参数模板

  3. 必须定义个type来表明自己的类型

  4. 没有默认和拷贝构造要求

  5. 没有继承的要求。

std::is_void

为了查看type_traits是如何实现的,我们首先来看看一个简单的例子:is_void,他判断一个类型是不是void!

template<typename T>
struct is_void : std::false_type{}; // 泛化是false
template<> // 特化
struct is_void<void> : std::true_type{}; // 特化为true_type

那问题来了,你试试看这样能不能通过static_assert呢?

#include <type_traits>
template<typename T>
struct My_is_void : std::false_type{};
​
template<>
struct My_is_void<void> : std::true_type{};
​
int main()
{static_assert(My_is_void<void>::value);static_assert(My_is_void<void const>::value);
}

啊哈,结果很明显了,并不会,一种办法有点蠢:

#include <type_traits>
template<typename T>
struct My_is_void : std::false_type{};
​
template<>
struct My_is_void<void> : std::true_type{};
​
template<>
struct My_is_void<void const> : std::true_type{};
​
template<>
struct My_is_void<void volatile> : std::true_type{};
​
template<>
struct My_is_void<void volatile const> : std::true_type{};
​
int main()
{static_assert(My_is_void<void>::value);static_assert(My_is_void<void const>::value);
}

但是看着很抽象,硬编码,不是一个很好的解决方案

Transformation traits

所以我们有了类型变换元函数,请看例子:

remove_const
remove_const<int> -> int
remove_const<const int> -> int
remove_const<const volatile int> -> volatile int;
remove_const<int*> -> int*
​
// Important
remove_const<const int*> -> const int* // 指向const int,指针本身不const
remove_const<int* const> -> int*

那如何实现呢?很简单,只需要让带有修饰符号的模板特殊匹配就好:

template<typename T>
struct remove_const : type_identity<T>{}
​
template<typename T>
struct remove_const<T const> : type_identity<T>{}
​
// use _t
template<typename T>
using remove_const_t = typename remove_const<T>::type;

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

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

相关文章

LAMDA面试准备(2024-05-23)

有没有学习过机器学习&#xff0c;提问了 FP-Growth 相比 Apriori 的优点 1. 更高的效率和更少的计算量&#xff08;时间&#xff09; FP-Growth 通过构建和遍历 FP-树 (Frequent Pattern Tree) 来挖掘频繁项集&#xff0c;而不需要像 Apriori 那样生成和测试大量的候选项集。具…

【阿里前端面试题】知道了解浏览器渲染对自己有什么帮助?

大家好&#xff0c;我是“寻找DX3906”。每天进步一点。日积月累&#xff0c;有朝一日定会厚积薄发&#xff01; 前言&#xff1a; 前面已经和大家分享了3篇面试题&#xff1a; 《【阿里前端面试题】浏览器的加载渲染过程》 《【阿里前端面试题】客户端和服务器交互&#xff0…

5.23.2 深度学习提高乳房 X 光检查中乳腺癌的检测率

开发了一种深度学习算法&#xff0c;该算法可以使用“端到端”训练方法在筛查乳房 X 光检查中准确检测出乳腺癌&#xff0c;该方法有效地利用了具有完整临床注释或仅具有整个图像的癌症 标签 的训练数据集。 在这种方法中&#xff0c;仅在初始训练阶段才需要病变注释&#xff…

springboot vue 开源 会员收银系统 (2) 搭建基础框架

前言 完整版演示 前面我们对会员系统https://blog.csdn.net/qq_35238367/article/details/126174288进行了分析 确定了技术选型 和基本的模块 下面我们将从 springboot脚手架开发一套收银系统 使用脚手架的好处 不用编写基础的rabc权限系统将工作量回归业务本身生成代码 便于…

conda使用常用命令

Conda是一个非常常用的Python包管理器&#xff0c;也是Anaconda Python发行版的一部分。它可以帮助用户安装、更新、卸载Python包&#xff0c;以及管理Python虚拟环境。在这篇博客中&#xff0c;我们将总结一些常用的Conda命令及其用法。 安装和更新Conda 在使用Conda之前&…

React 性能优化

性能优化 React 性能优化方案&#xff0c;说出来可能有很多&#xff0c;但是那些是所有前端项目、框架都需要做的。如 代码分割&#xff0c;代码压缩&#xff0c;使用生产版本的代码等&#xff08;前端框架都需要&#xff0c;通用方案&#xff09; 渲染列表时合理使用 key &a…

Tensorflow入门实战 P01-实现手写数字识别mnist

目录 1、背景&#xff1a;MNIST手写数字识别 2、完整代码&#xff08;Tensorflow&#xff09;&#xff1a; 3、运行过程及结果&#xff1a; 4、小结&#xff08;还是很清晰的&#xff09; 5、 展望 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客…

Kafka之【生产消息】

消息&#xff08;Record&#xff09; 在kafka中传递的数据我们称之为消息&#xff08;message&#xff09;或记录(record)&#xff0c;所以Kafka发送数据前&#xff0c;需要将待发送的数据封装为指定的数据模型&#xff1a; 相关属性必须在构建数据模型时指定&#xff0c;其中…

深入了解YUM:原理、作用及异常处理最佳实践20240523

深入了解YUM&#xff1a;原理、作用及异常处理最佳实践 引言 作为一名Linux系统管理员或开发者&#xff0c;你一定对软件包管理有深刻的理解。在CentOS等基于RPM的系统中&#xff0c;YUM&#xff08;Yellowdog Updater, Modified&#xff09;是一个强大的包管理工具。它不仅简…

本地spark3.5(不整合hive) 集成paimon0.9

spark官网下载集成hadoop的spark包: spark-3.5.1-bin-hadoop3.... 解压后 环境变量配置 SPARK_HOME spark-defaults.conf 中增加一行配置(避免启动spark-sql报错hive元数据连不上): spark.sql.catalogImplementationhive 打开paimon官网: https://paimon.apache.org/docs/mas…

JavaEE技术之分布式事务(理论、解决方案、Seata解决分布式事务问题、Seata之原理简介、断点查看数据库表数据变化)

文章目录 JavaEE技术之分布式事务准备:1. 本地事务回顾1.1 什么是事务1.2 事务的作用1.3 事务ACID四大特性1.4 事务的并发问题1.5 MySQL事务隔离级别1.6 事务相关命令(了解)1.7 事务传播行为&#xff08;propagation behavior&#xff09;1.8 伪代码练习1.9 回滚策略1.10 超时事…

144.栈和队列:有效的括号(力扣)

题目描述 代码解决 class Solution { public:bool isValid(string s) {// 如果字符串长度为奇数&#xff0c;不可能是有效的括号字符串if(s.size() % 2 ! 0) return false;// 使用栈来存放括号stack<char> st;// 遍历字符串中的每一个字符for(int i 0; i < s.size();…

Error:(6, 43) java: 程序包org.springframework.data.redis.core不存在

目录 一、在做SpringBoot整合Redis的项目时&#xff0c;报错&#xff1a; 二、尝试 三、解决办法 一、在做SpringBoot整合Redis的项目时&#xff0c;报错&#xff1a; 二、尝试 给依赖加版本号&#xff0c;并且把版本换了个遍&#xff0c;也不行&#xff0c;也去update过ma…

MYSQL变更性别

Salary 表&#xff1a; ----------------------- | Column Name | Type | ----------------------- | id | int | | name | varchar | | sex | ENUM | | salary | int | ----------------------- id 是这个表的主键&#xff…

Parasoft C++Test软件静态分析操作指南_软件质量度量

系列文章目录 Parasoft CTest软件安装指南 Parasoft CTest软件静态分析操作指南_编码规范/标准检查 Parasoft CTest软件静态分析操作指南_软件质量度量 Parasoft CTest软件静态分析_自动提取静态分析数据生成文档 Parasoft CTest软件单元测试_操作指南 Parasoft CTest软件单元…

C语言章节学习归纳--数据类型、运算符与表达式

3.1 C语言的数据类型&#xff08;理解&#xff09; 首先&#xff0c;对变量的定义可以包括三个方面&#xff1a; 数据类型 存储类型 作用域 所谓数据类型是按被定义变量的性质&#xff0c;表示形式&#xff0c;占据存储空间的多少&#xff0c;构造特点来划分的。在C语言中&…

2461. 长度为 K 子数组中的最大和(c++)

给你一个整数数组 nums 和一个整数 k 。请你从 nums 中满足下述条件的全部子数组中找出最大子数组和&#xff1a; 子数组的长度是 k&#xff0c;且子数组中的所有元素 各不相同 。 返回满足题面要求的最大子数组和。如果不存在子数组满足这些条件&#xff0c;返回 0 。 子数…

设计模式6——单例模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 单例模式&#xff08;Singleto…

完成商品属性分组和商品属性关联维护

文章目录 1.前端页面搭建1.复制attrgroup-attr-relation.vue到src/views/modules/commodity下2.加入超链接和引入组件 src/views/modules/commodity/attrgroup.vue1.加入超链接2.引入组件 3.数据池加入变量4.使用组件1.引用组件2.添加方法3.测试&#xff0c;点击关联&#xff0…

建站平台布局结构

建站平台布局结构对于网站的成功至关重要。一个良好的布局结构能够有效地吸引用户&#xff0c;提升用户体验&#xff0c;并且有助于网站的搜索引擎优化&#xff08;SEO&#xff09;。在设计网站布局结构时&#xff0c;需要考虑到用户导航、信息层次结构、页面加载速度等方面&am…