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.mfbz.cn/a/638503.html

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

相关文章

27寸2K显示器 - HKC G27H2

HKC G27H2是一款面向电竞市场的高性能显示器&#xff0c;以其2K分辨率和180Hz的刷新率作为主要卖点&#xff0c;旨在为玩家提供流畅而清晰的视觉体验。配备HDR 400技术和95% DCI-P3色域覆盖&#xff0c;这款显示器还支持升降旋转支架&#xff0c;为用户提供了高度的人体工程学适…

微软开源多模态大模型Phi-3-vision,微调实战来了

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型& AIGC 技术趋势、大模型& AIGC 落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了…

继承初级入门复习

注意&#xff1a;保护和私有在类中没有区别&#xff0c;但是在继承中有区别&#xff0c;private在继承的子类不可见&#xff0c;protect在继承的子类可见 记忆方法&#xff1a;先看基类的修饰符是private&#xff0c;那都是不可见的。如果不是&#xff0c;那就用继承的修饰和基…

知了传课Flask学习(持续更新)

一、基础内容 1.Flask快速应用 pip install flask from flask import Flaskapp Flask(__name__)app.route(/) def index():return Hello worldif __name__ __main__:app.run() 2.debug、host、port配置 from flask import Flask,requestapp Flask(__name__)app.route(/) d…

在洁净实验室设计装修中怎么选择合适实验室家具?

在现代科学研究和技术开发中&#xff0c;洁净实验室装修设计成为了确保实验准确性和安全性的重要因素。洁净实验室需要提供一个无尘、无菌、受控的环境&#xff0c;而在洁净实验室装修设计这个过程中&#xff0c;如何选择合适的实验室家具就显得尤为重要&#xff0c;因为它直接…

第一行代码 按书配置Menu不出来

问题&#xff1a;按照书本配置Menu&#xff0c;就是不出来 页面activity 源码 重写了&#xff1a;onCreateOptionsMenu(), onOptionsItemSelected() package com.example.lanidemoktimport android.os.Bundle import android.util.Log import android.view.Menu import andro…

欢乐钓鱼大师攻略大全,游戏自动辅助,钓鱼大全!

欢迎来到《欢乐钓鱼大师》的攻略大全&#xff01;本文将为你详细介绍游戏中的各类玩法、技巧和注意事项&#xff0c;帮助你快速掌握游戏精髓&#xff0c;成为一名真正的钓鱼大师。攻略内容包括新手鱼竿选择、锦标赛攻略、实用技巧、藏宝图玩法、箱子开法等多个方面。让我们一起…

个人博客网站开发笔记3

文章目录 前言p4 Front Matterp5 配置文件p6 命令p7 部署新的教学视频部署博客到github找视频教程也是一个技能详细步骤安装主题安装渲染器修改主题创建gitub仓库生成密钥验证密钥是否匹配修改config文件推送到github 前言 主要是安装啥的比较费劲 现在已经比较简单了感觉 之…

面试问题小结

说说你的项目&#xff0c;从里面学到啥了&#xff08;随便说&#xff09; CAS 线程池 的各个方面 线程咋创建&#xff08;4种方式&#xff09; 说一下聚集索引和非聚集索引 50w男 50w女 &#xff0c;在B树中咋存储的&#xff08;类似下面的图&#xff0c;变通一下就行了&a…

WXML模板语法-事件绑定

一、 1.事件 事件是渲染层到逻辑层的通讯方式&#xff0c;通过事件可以将用户在渲染层产生的行为&#xff0c;反馈到逻辑层进行业务的处理 2.小程序中常用的事件 3.事件对象的属性列表 当事件回调触发的时候&#xff0c;会收到一个事件对象event&#xff0c;其属性为&#x…

一文带你入门ini格式

引入: 以蜂鸣器为例&#xff0c;每次我们增加新的设备&#xff0c; 都需要添加两个新文件: 修改程序代码&#xff0c;手动添加: 缺点: 不利于维护 设备类节点直接通过ini文件配置 什么是.ini文件 ini文件通常以纯文本形式存在&#xff0c;并且包含了一个或多个节&#xff08;se…

快速搭建流媒体服务

1、安装流媒体服务 源码地址&#xff1a;https://gitee.com/ossrs/srs 本次采用docker安装 docker run --rm -it -p 1935:1935 -p 1985:1985 -p 8080:8080 -p 8000:8000/udp -p 10080:10080/udp registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5 查看运行效果&#xff…

[LLM-Agents]浅析Agent工具使用框架:MM-ReAct

上文LLM-Agents]详解Agent中工具使用Workflow提到MM-ReAct框架&#xff0c;通过结合ChatGPT 与视觉专家模型来解决复杂的视觉理解任务的框架。通过设计文本提示&#xff08;prompt design&#xff09;&#xff0c;使得语言模型能够接受、关联和处理多模态信息&#xff0c;如图像…

QQ技术导航源码附带交易系统

网站功能 QQ登录 友联自助交换 友情链接交易功能 多功能搜索 ico小图标本地化 网站图片本地化 蜘蛛日志 文章评论 网站评论 自助链接匿名提交站点&#xff0c;添加友链访问网站自动审核通过 VIP 会员等级 VIP 付费升级 单个文章或者站点付费快审 多背景图片可自定义背景图片…

【数据结构】第七节:堆

个人主页&#xff1a; 深情秋刀鱼-CSDN博客 数据结构专栏&#xff1a;数据结构与算法 源码获取&#xff1a;数据结构: 上传我写的关于数据结构的代码 (gitee.com) ​ 目录 一、堆 1.堆的概念 2.堆的定义 二、堆的实现 1.初始化和销毁 2.插入 向上调整算法 3.删除 向下调整算法…

9.STL中list的常见操作(图文并茂)

目录 1.list的介绍及使用 1.1.list的构造 1.2 list iterator的使用 1.3. list capacity 1.4.list modifiers 1.5.list的迭代器失效 1.list的介绍及使用 list介绍 &#xff0c;可以通过以下图直观的感受到 vector 和 list 的区别 Vector 插入代价高&#xff0c;但便于排…

LabVIEW与串口通讯在运行一段时间后出现数据接收中断的问题

这些问题可能与硬件、软件或通信协议有关。以下是详细的原因分析和可能的解决方案&#xff1a; 一、硬件原因 串口线缆或接口问题&#xff1a; 由于长时间使用&#xff0c;串口线缆可能出现接触不良或损坏。接口松动也可能导致通讯中断。 解决方案&#xff1a;检查并更换串口…

【区块链】智能合约漏洞测试

打开Ganache vscode打开智能合约漏洞工程 合约内容 pragma solidity >0.8.3;contract EtherStore {mapping(address > uint) public balances;function deposit() public payable {balances[msg.sender] msg.value;emit Balance(balances[msg.sender]);}function with…

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

文章目录 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…

【笔记】Qt 按钮控件介绍(QPushButton,QCheckBox,QToolButton)

文章目录 QAbstractButton 抽象类(父类)QAbstractButton 类中的属性QAbstractButton 类中的函数QAbstractButton 类中的信号QAbstractButton 类中的槽 QPushButton 类(标准按钮)QPushButton 类中的属性QPushButton 类中的函数、槽 QCheckBox 类(复选按钮)QCheckBox 类的属性QCh…