c++—可变参数、强枚举、多类型存储(variant)、动态类型(any)、和类型(optional)

news/2024/7/24 13:27:35 标签: c++, 开发语言, linux, c语言

1. 可变参数

    (1)C语言中的可变参数,其原理是利用所有的参数在找空间的保存位置是连续的;内部的程序主要是有va_list指针;

    (2)c++的可变参数,就是指initializer_list列表初始化,本质是一个表示某种特定类型的值的数组,可以理解为一个不可扩充的容器;

        ①初始化的区别:这就是为什么很多c++书籍喜欢用{}进型初始化的原因,因为检查严格;

int c = 3.3;  //符合,因为系统内会隐式转换,去尾,C语言风格
int b = {3.3};  //不符合,因为这里默认的是initializer_list<double>,无法实现double到int的转换;
int a {4.5};    //不符合,因为这里默认的是initializer_list<double>,无法实现double到int的转换;

        ②优点:有严格的类型检查,不会允许不同类型之间的隐式转换,这一点不同于C语言;

        ③用处:主要用在构造函数,把初始化列表当做参数,示例如下:

#include <iostream>

using namespace std;

class A
{
public:
    A(int num):m_num(num)  
    {
    }

    A(const initializer_list<int> v)  //这里是自定义的,与系统默认生成的带可变参数的构造函数相同,不同的是自定义的可以传多个参数,而系统默认的则只能根据属性(m_t的个数)传递;
    {
        for(auto temp : v)
        {
            cout<<temp<<endl;
        }
    }

    int m_num; 
};

void func1(initializer_list<int> t)    //传进来的是一个可变参数列表,需要通过迭代器遍历
{
    //cout<<t<<endl;
    auto it = t.begin();
    for(; it != t.end(); it++)
    {
        cout<<*it<<endl;
    }
}

int main(int argc, char **argv)
{
    initializer_list<int>init_list = {1,3,5,7,9};
    auto it = init_list.begin();    //可变参数列表不支持[],需要利用迭代器
    for(; it!= init_list.end(); it++)
    {
        cout<<*it<<endl;
    }

    func1({2,4,6,8,10});

    A a(1);
    A a1{200};  //此时,类A中默认生成含有初始化列表的默认构造函数;当没有自定义的initializer_list时,这里的200相当于给m_num赋值,当有时,则不是赋值;
    cout<<a1.m_num<<endl;
    A a2{3,5,7,9};
    cout<<a2.m_num<<endl;

    int count = {3};      //带{}的,系统内转化成了initializer_list<int>
    // int len = {3.14};  //initializer_list<double> v = 3.14,无法将double赋给int
    // int m_count {3.3};  //同样会报错,存在double到int的不合适转化,这里不同于C语言的隐式转换

    return 0;
}

2. 强枚举类型

    (1)和C语言的枚举类型相比,优势:

        ①强作用域:使用类型成员的时候必须前缀上所属的枚举类型的名字;

        ②可以执行底层类型:枚举类型后面使用了冒号以及类型关键字来执行枚举中的枚举值的类型

        ③转换限制(本质原因是强枚举是一种类型,成员尽管设置成int,但还是该枚举类型下的int,与外部的int不同):强类型枚举成员的值不可以与整型变量进行转换(隐式与显式都不可以)、不能将其与整数数字进行比较、不能对不同的枚举类型的枚举值进行比较、相同类型的枚举值可以比较;示例如下

#include <iostream>

using namespace std;

enum RET1{OK=1,NO=2};
enum class RET2:int{OKK=1,NOO=2};  //表示了指定底层类型的优点

RET2 test(int num)  //返回值的类型似乎enum枚举类型,这里不能写作int
{
    if(num == 5)
    {
        return RET2::OKK;
    }
    else
    {
        return RET2::NOO;
    }
}

int main(int argc, char **argv)
{
    RET2 n = test(7);  //注意枚举也是一种数据类型,这里不能用作int
    if(n == RET2::OKK) //表示了强作用域的优点,需要前缀枚举类型名称;相同的枚举类型之间可以比较;
    {
        cout<<"等于"<<endl;
    }
    else
    {
        cout<<"不等于"<<endl; 
    }

    // int n = RET2::OKK;  //不能利用强枚举类型的成员与整型进行转换,隐式与显式的都不可以

    return 0;
}

3. 多类型存储(variant)

    (1)variant 产生的原因是C语言中的union,其成员只能是同一种类型;而variant则优化了该问题, 是一个类型安全的联合体,本质是一个类模板;其特点是①当前值的类型总是已知的(有成员函数方法判断);②可以有任何指定类型的成员;③因为其是一个类,所以可以派生类;

    (2)注意事项

        ①variant不容许保有引用、数组,或类型void;

        ②variant容许保有同一类型多于一次,而且保有同一类型的不同cv限定版本;

        ③默认构造的variant保有其首个选项的值,除非该选型不是可默认构造的??;

    (3)当vatiant作为参数传递时,如何知道此时共用体varitant此时保存的是什么类型的成员?可以利用std::holds_allterative<int>(v),如果此时传来的共用体保存的是int类型的成员,那么该语句就是true,示例如下:

#include <iostream>
#include <variant>

using namespace std;

using p_type = std::variant<int ,string, double>;

void print(p_type &v)
{
    if(std::holds_alternative<int>(v))  //利用std::holds_alternative<>判断传来的共用体里面是什么类型的值
    {
        cout<<v.index()<<" ";  //输出索引号
        cout<<std::get<int>(v)<<endl;
    }
    if(std::holds_alternative<string>(v))
    {
        cout<<v.index()<<" ";
        cout<<std::get<string>(v)<<endl;
    }
    if(std::holds_alternative<double>(v))
    {
        cout<<v.index()<<" ";
        cout<<std::get<double>(v)<<endl;
    }
}

int main(int argc, char **argv)
{
    p_type v;
    v = 7;
    print(v);

    v = "hello";
    print(v);

    v = 3.14;
    print(v);
    //std::variant<A,int ,string, double> v2;//无法引用 "std::variant<A, int, std::string, double>" 的默认构造函数 -- 它是已删除的函数C/C++(1790)
    std::variant<std::monostate,int ,string, double> v2; //若第一个共用体成员是类型,则要求该类型需要有默认构造函数,因为类型A没有了默认构造函数,所以需要std::monostate占位符,同时不会影响共用体varitant的内部结构,单纯站位,避免报错    

return 0;
}

    (3)常用方法(成员函数与非成员函数)

        成员函数:

        ①index():输出当前共用体成员在variant共用体中的索引(0-n);

        ②emplace():原地构造函数??

        非成员函数:

        ①

        ②


http://www.niftyadmin.cn/n/377247.html

相关文章

Mac 安装homebrew Mac安装Git

文章目录 1 homebrew介绍2 下载Brew2.1 使用中文镜像进行安装 3 配置brew环境变量4 更换镜像源5 安装Git参考 1 homebrew介绍 Homebrew是一款包管理工具&#xff0c;目前支持macOS和Linux系统 主要有四个部分组成&#xff1a; brewhomebrew-corehomebrew-caskhomebrew-bottle…

关于 String 那些不得不说的那些事

一、String、StringBuilder和StringBuffer的区别 1. String是字符串常量&#xff0c;StringBuilder和StringBuffer是字符串变量 String对象创建完成之后&#xff0c;如果对其更改&#xff0c;都是重新创建一个字符串对象&#xff0c;让引用变量重新指向其引用地址&#xff0c…

灵活使用Postman环境变量和全局变量,提高接口测试效率!

目录 前言&#xff1a; 环境变量和全局变量的概念 环境变量和全局变量的使用方法 1. 定义变量 2. 使用变量 环境变量和全局变量的实例代码 变量的继承和覆盖 变量的动态设置 总结&#xff1a; 前言&#xff1a; Postman是一个流行的API开发和接口测试工具&#xff0c;…

超强实用!利用xfsdump和xfsrestore打造无懈可击的数据备份与恢复策略

前言 上次我们分析了EXT文件系统的恢复方式&#xff0c;借助于extundelete工具仅可以恢复EXT类型的文件&#xff0c;但无法恢复CentOS 7系统&#xff0c;因为centos7默认采用xfs类型的文件。 xfs文件系统恢复工具有以下几种&#xff1a; xfsprogs&#xff1a;xfs文件系统扩展…

PointNet++ 源码解读

1.从main函数开始&#xff1a; 1.1 确定使用的哪个GPU. 1.2 保存训练时的参数和日志 2. 加载数据 先找到存放训练和测试数据的目录&#xff0c;接下来加载相关的数据参数&#xff1a; 下面是执行的结果&#xff1a; 接下来为训练样本开始做准备&#xff1a; 给不同标签做上标记…

HBase集群搭建

hbase 1.解压HBase安装包 先 下载HBase压缩包&#xff0c;并解压安装文件&#xff0c;示例代码如下&#xff1a; tar -zxvf hbase-2.0.1-bin.tar.gz2. 修改配置文件 编辑 conf目录下的 hbase-env.sh文件&#xff0c;示例代码如下&#xff1a; cd conf vi hbase-env.sh添加…

机器学习实战7-基于机器学习算法预测相亲成功率

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战7-基于机器学习算法预测相亲成功率&#xff0c;随着社会的发展&#xff0c;大家都忙于事业&#xff0c;对自己的终身大事就耽搁了&#xff0c;相亲是一种传统的寻找伴侣的方式&#xff0c;随着时代的发…

静默安装oracle

oracle依赖环境包 一、创建用户属组 [rootlocalhost ~]# groupadd oinstall[rootlocalhost ~]# groupadd dba[rootlocalhost ~]# groupadd oper[rootlocalhost ~]# useradd -g oinstall -G dba,oper oracle[rootlocalhost ~]# passwd oracle #修改oracle用户密码 二、创建目录…