CPP Con 2020:Type Traits I

news2024/12/22 14:31:03

先谈谈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.coloradmin.cn/o/1690372.html

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

相关文章

Python实现将LabelMe生成的JSON格式转换成YOLOv8支持的TXT格式

标注工具 LabelMe 生成的标注文件为JSON格式&#xff0c;而YOLOv8中支持的为TXT文件格式。以下Python代码实现3个功能&#xff1a; 1.将JSON格式转换成TXT格式&#xff1b; 2.将数据集进行随机拆分&#xff0c;生成YOLOv8支持的目录结构&#xff1b; 3.生成YOLOv8支持的YAML文件…

探索亚马逊云科技技术课程:大模型平台与提示工程的应用与优化

上方图片源自亚马逊云科技【生成式 AI 精英速成计划】技术开发技能课程 前言 学习了亚马逊云科技–技术开发技能课程 本课程分为三个部分&#xff0c;了解如何使用大模型平台、如何训练与部署大模型及生成式AI产品应用与开发&#xff0c;了解各类服务的优势、功能、典型使用案…

【QT八股文】系列之篇章2 | QT的信号与槽机制及通讯流程

【QT八股文】系列之篇章2 | QT的信号与槽机制及通讯流程 前言2. 信号与槽信号与槽机制介绍/本质/原理&#xff0c;什么是Qt信号与槽机制&#xff1f;如何在Qt中使用&#xff1f;信号与槽机制原理&#xff0c;解析流程Qt信号槽的调用流程信号与槽机制的优缺点信号与槽机制需要注…

【软考中级 软件设计师】数据结构

数据结构是计算机科学中一个基础且重要的概念&#xff0c;它研究数据的存储结构以及在此结构上执行的各种操作。在准备软考中级-软件设计师考试时&#xff0c;掌握好数据结构部分对于通过考试至关重要。下面是一些核心知识点概览&#xff1a; 基本概念&#xff1a; 数据结构定义…

在NVIDIA Jetson Nano上部署YOLOv5算法,并使用TensorRT和DeepStream进行加速

部署YOLOv5算法在NVIDIA Jetson Nano上并使用TensorRT和DeepStream进行加速涉及几个关键步骤。下面是一个详细的指南&#xff1a; 步骤 1: 准备YOLOv5模型 训练或下载预训练模型&#xff1a;首先&#xff0c;你需要有一个YOLOv5模型。你可以自己训练一个模型&#xff0c;或者…

响应式处理-一篇打尽

纯pc端响应式 pc端平常用到的响应式布局 大致就如下三种&#xff0c;当然也会有其他方法&#xff0c;欢迎评论区补充 将div height、width设置成100% flex布局 flex布局主要是将flex-wrap: wrap&#xff0c; 最后&#xff0c;你可以通过给子元素设置 flex 属性来控制它们的…

构建全面的无障碍学习环境:科技之光,照亮学习之旅

在信息与科技日益发展的当下&#xff0c;为所有人群提供一个包容和平等的学习环境显得尤为重要&#xff0c;特别是对于盲人朋友而言&#xff0c;无障碍学习环境的构建成为了一项亟待关注与深化的课题。一款名为“蝙蝠避障”的辅助软件&#xff0c;以其创新的设计理念与实用功能…

Excel 按顺序去重再编号

Excel的A有重复数据&#xff1a; A1Cow2Chicken3Horse4Butterfly5Cow 现在要去除重复&#xff0c;用自然数按顺序进行编号&#xff0c;结果写在相邻列&#xff1a; AB1Cow12Chicken23Horse34Butterfly45Cow1 使用 SPL XLL&#xff0c;输入公式并向下拖&#xff1a; spl(&q…

云平台的安全能力提升解决方案

提升云平台的安全能力是确保数据和服务安全的关键步骤。针对大型云平台所面临的云上安全建设问题&#xff0c;安全狗提供完整的一站式云安全解决方案&#xff0c;充分匹配云平台安全管理方的需求和云租户的安全需求。协助大型云平台建设全网安全态势感知、统一风险管理、统一资…

Zabbix-agents (windows环境)安装及配置

目录 一. 简介 Zabbix 服务端 1. Zabbix 服务器&#xff08;Server&#xff09; 2. Zabbix 数据库 3. Zabbix Web 前端 Zabbix 客户端 1. Zabbix 代理&#xff08;Agent&#xff09; 2. 安装和配置 二. 下载 三. 安装 四. 检查是否启动 五. 手动启动方式 六 .创建…

Python面向对象数据库之ZODB使用详解

概要 ZODB(Zope Object Database)是一个纯Python的面向对象数据库。它允许程序员将Python对象以透明的方式存储在数据库中,无需将对象模型转换为关系模型,极大地简化了Python应用的数据持久化工作。 安装 安装ZODB非常简单,可以通过Python的包管理器pip进行安装: pip …

leecode热题100---994:腐烂的橘子

题目&#xff1a; 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b; 值 1 代表新鲜橘子&#xff1b; 值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回…

ABP.VNET 项目结构

想要了解ABP分层架构&#xff1a;ABP分层架构-CSDN博客 可以看此篇文章 apb.vnet 生成的项目的目录结构 .Application 项目 应用服务用于将领域(业务)逻辑暴露给展现层。 展现层通过传入DTO(数据传输对象)参数来调用应用服务&#xff0c;而应用服务通过领域对象来执行相应的…

webSocket+Node+Js实现在线聊天(包含所有代码)

这篇文章主要介绍了如何使用 webSocket、Node 和 Js 实现在线聊天功能。 重要亮点 &#x1f4bb; 技术选型&#xff1a;使用 Node.js 搭建服务器&#xff0c;利用 Express 框架和 Socket.io 库实现 WebSocket 通信。 &#x1f4c4; 实现思路&#xff1a;通过建立数组存储聊天…

Go语言直接使用Windows的IOCP API写一个echo服务器

Go的标准库中Windows下的网络是使用了IOCP的&#xff0c;参见go源码go/src/runtime/netpoll_windows.go&#xff0c;标准库为了与Epoll、kqueue等不同平台的IO模式使用统一的API&#xff0c;进行了封装。 如果想直接使用Windows的IOCP API编程&#xff0c;比如想按照&#xff…

场内期权怎么开户?佣金手续费最低是多少?

今天期权懂带你了解场内期权怎么开户&#xff1f;佣金手续费最低是多少&#xff1f;我国的首个场内期权是50ETF期权&#xff0c;随着投资者对期权产品日渐熟悉&#xff0c;投资者参与数量与交易量稳步增长。 场内期权怎么开户&#xff1f; 满足资金要求&#xff1a;根据监管要…

DLT645-97/07通信规约 | 报文解析 | 组织报文与解析报文(C++)

文章目录 一、DLT645通信规约1.DLT645-1997通信规约2.DLT645-2007通讯规约3.DLT645-1997与DLT645-2007的区别 二、DLT645通信规约报文解析1.DLT645-1997通信规约报文解析2.DLT645-2007通信规约报文解析 三、C代码组织报文与解析报文 一、DLT645通信规约 DLT645协议&#xff0c;…

LeetCode 131题详解:高效分割回文串的递归与动态规划方法

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

每日一题(1)

在看一本08年出版的书的时候&#xff0c;看到了这样一个问题&#xff0c;感觉答案很奇怪&#xff1a; public class demo_p22 {public static void main(String args[]){int sCook1,sFish2;//各技能标记character ch1new character();if(ch1.haveSkill(sCook))System.out.print…