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 那样生成和测试大量的候选项集。具…

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

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

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

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

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;其中…

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…

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…

Android JetPack快速上手

学习地址 【Android Jetpack组件从入门到入坟&#xff0c;全家桶全面学习教程精讲&#xff0c;通俗易懂】 review 研究生期间接触过一部分android开发&#xff0c;近期有个小项目需要进行开发&#xff0c;临时恶补了一下Android相关知识点&#xff0c;突然发现Android新增了…

VBA语言専攻每周通知20240524

通知20240524 各位学员∶本周MF系列VBA技术资料增加611-615讲&#xff0c;T3学员看到通知后请免费领取,领取时间5月24日晚上18:00-5月26日晚上18:00。本次增加内容&#xff1a; MF611:用InputBox录入日期 MF612:信息提示10秒后关自动关闭 MF613:只是信息提示10秒 MF614:显…

如何解决Nginx反向代理不生效?

目录 背景 过程 日志 检查配置文件 重启服务 检查容器内的配置文件 容器和宿主机 其他 背景 用了两年的nginx新加的反向代理不生效 Docker挂载的配置文件启动的Nginx&#xff0c;配置一切正常&#xff0c;但是反向代理不生效&#xff0c;???先自查一波 过程 日志 …

RDDM论文阅读笔记

CVPR2024的残差去噪模型。把diffusion 模型的加噪过程分解为残差diffusion和noise diffusion&#xff0c;其中残差diffusion模拟从target image到degraded image的过程&#xff0c;而noise diffusion则是原来的diffusion过程&#xff0c;即从图片到高斯噪声的加噪过程。前者可以…

如何让社区版IDEA变得好用

如何让社区版IDEA变得好用 背景 收费版的idea功能非常强大&#xff0c;但是费用高。社区版的免费&#xff0c;但是功能被阉割了。如何才能让社区版Idea变得好用&#xff0c;就需要各种插件支持了。经过全局配置编码&#xff0c;maven&#xff0c;jdk版本&#xff0c;在加上各…

架构二。。

1、CAP 只能3选2 1&#xff09;一致性&#xff08;Consistency&#xff09; 客户每次读都是返回最新的写操作结果 2&#xff09;可用性&#xff08;Availability&#xff09; 非故障节点在合理的时间内返回合理的响应 3&#xff09;分区容忍性&#xff08;Partition Tolerance…