首页 > 编程语言 >C++进阶学习(三)constexpr关键字、值类别与decltype关键字、lambda表达式

C++进阶学习(三)constexpr关键字、值类别与decltype关键字、lambda表达式

时间:2023-05-17 17:12:11浏览次数:33  
标签:decltype 进阶 constexpr int auto --- 关键字 lambda cout

五、constexpr说明符

  • constexpr说明符声明该变量或函数在编译期进行求值,从而适用于需要编译器常量表达式的地方
  • 在变量声明constexpr时,对象或非静态成员函数蕴含const,函数或静态成员变量蕴含inline
  • constexpr变量必须立刻被初始化

constexpr int a = 5;

// a = 6; /*error*/

 

  • 如果一个函数或模板的一个声明具有constexpr,那么该函数或模板的所有声明都必须具有constexpr
  • const承担“只读”的修饰,而constexpr承担“常量”的修饰,并且在编译期就可以求出该变量或函数的值

int num = 5;

// constexpr auto n1 = num; /*error*/

// constexpr array<int, num> arr1(); /*error*/

constexpr auto n2 = 4;

constexpr array<int, n2> arr2();

在上面的例子中,n1与arr1的定义都需要num的参与,而num在编译期无法获得它的值,故无法正确定义n1和arr1。而n2是constexpr的,故arr2在编译期可以得到n2的值。

 

六、值类别与decltype

  • 每个表达式都属于三种基本值类别中的一种:纯右值、亡值、左值
  • 我们可以通过decltype判断一个表达式的值类别,若产生T&&,则为亡值;若产生T&,则为左值;若产生T,则为纯右值
  • 除了字符串字面量,其他所有字面量都为纯右值字面量
  • const T&方式的左值引用可以接收右值(纯右值和亡值)

int operator""_i(size_t num)

{

    return num;

}

 

int num(int n)

{

    return n;

}

 

void func(const int &num)

{

    cout << num << endl;

}

 

int main()

{

    cout << "---begin---" << endl;

    int val{0};

    // 以下表达式均能传参成功,即均为右值

    func(val);

    func(num(5));

    func(move(val));

    func(12_i);

    cout << "---end---" << endl;

    return 0;

}

  • 使用decltype时,带有括号的对象通常被认为是左值表达式

int main()

{

    cout << "---begin---" << endl;

    int val{0};

    using T1 = decltype((val));

    using T2 = decltype(val);

    T2 b = 5;

    T1 a = b;

    cout << &a << endl;

    cout << &b << endl;

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

0x8f29bff8ac

0x8f29bff8ac

---end---

可以看出,当我们把b赋值给a后,两者的地址相同,易见T1是T2的引用类型。

 

七、lambda表达式

  • lambda是一个无名非联合非聚合类类型,被称为闭包类型,类的操作同样适用于它(注:下列代码只适用于C++20之后的版本)

auto lambda = [](int n)

{ cout << n << endl; };

// 继承适用

struct Test : decltype(lambda){};

 

int main()

{

    cout << "---begin---" << endl;

    Test test;

    test(1);

 

    // 指针适用

    auto ptr = make_shared<decltype(lambda)>(lambda);

    (*ptr)(2);

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

1

2

---end---

 

  • lambda对象常用auto进行自动类型推导
  • lambda捕获符只有=和&,其中,=是以复制的形式进行捕获,&是通过引用的形式进行捕获
  • 捕获的组合应保证每个变量的捕获不重复,且名字不能与传入形参相同

int main()

{

    cout << "---begin---" << endl;

    int a = 5, b = 5;

 

    // 值传递捕获指定对象

    auto ptr1 = [a]

    {

        cout << a << endl;

        // b++; /*error*/

    };

    ptr1();

 

    // 引用传递捕获指定对象

    auto ptr2 = [&a]

    {

        cout << ++a << endl;

    };

    ptr2();

    cout << a << endl;

 

    // 值传递捕获所有变量

    auto ptr3 = [=]

    {

        cout << a << ' ' << b << endl;

        // a++, b++; /*error*/

    };

    ptr3();

 

    // 引用传递捕获所有变量

    auto ptr4 = [&]

    {

        cout << ++a << ' ' << ++b << endl;

    };

    ptr4();

 

    // 值传递捕获所有变量,引用传递捕获部分变量

    auto ptr5 = [=, &a]

    {

        cout << a << ' ' << b << endl;

        a++;

        // b++; /*error*/

    };

    ptr5();

 

    // auto ptr6 = [&, &a] {}; /*error:对a重复进行引用捕获*/

 

    // auto ptr7 = [&a](int a){}; /*error:捕获的变量名与传入形参名称相同*/

 

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

5

6

6

6 5

7 6

7 6

---end---

 

  • lambda捕获对象默认为const,若使用mutable进行修饰则可以去除const

int main()

{

    cout << "---begin---" << endl;

    // auto p = [num = 0]

    // {

    //     num++; /*error*/

    //     cout << num << endl;

    // };

    auto p = [num = 0] mutable

    {

        num++;

        cout << num << endl;

    };

    p();

    cout << "---end---" << endl;

    return 0;

}

 

  • 对所有局部变量进行捕获时,只会捕获lambda体中被调用的对象

int main()

{

    cout << "---begin---" << endl;

    int a = 5, b = 5;

 

    auto ptr1 = [=] {};

    auto ptr2 = [=]

    {

        cout << a << endl;

    };

    cout << "ptr1 memory:" << sizeof ptr1 << "\nptr2 memory:" << sizeof ptr2 << endl;

 

    cout << "---end---" << endl;

    return 0;

}

在上面的实例中,ptr1虽然为全域捕获,但lambda体中没有使用外部变量,因此实际上为空类,故只有1字节来进行定址;而ptr2中由于只使用了对象a,故内存大小为4。

 

  • 如果变量是非局部变量,或具有静态或线程局部存储期的时候,或该变量是以常量表达式初始化的引用,lambda表达式在使用该变量前不需要对其进行捕获

int main()

{

    cout << "---begin---" << endl;

    static int a = 5;

    int b = 5;

    auto ptr = [=]()

    {

        a++;

        // b++; /*error*/

    };

    ptr();

    cout << a << endl;

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

6

---end---

 

  • 如果变量满足下列条件时,lambda在读取它的值前不需要进行捕获:该变量具有const而非volatile的整型或枚举类型并已经用常量表达式初始化,或该变量是constexpr的且没有mutable成员

int main()

{

    cout << "---begin---" << endl;

    const int a = 5;

 

    auto ptr = []

    {

        cout << a << endl;

    };

    ptr();

    cout << "size:" << sizeof ptr << endl;

 

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

5

size:1

---end---

可以看到,lambda体可以正常读取a的值,但本身仍为一个空类,可见lambda在读取a前并没有捕获a。

但是,我们并不能直接使用该常量:

const int a = 5;

 

auto ptr = []

{

    // cout << &a << endl; /*error*/

};

 

  • 若外部对象在typeid中被使用,lambda体会捕获该const对象

int main()

{

    cout << "---begin---" << endl;

    const int a = 5;

 

    auto ptr = [=](auto n)

    {

        typeid(a + n);

    };

    cout << "size:" << sizeof ptr << endl;

 

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

size:4

---end---

 

  • 泛型lambda:在lambda的形参中使用auto传参,都会虚设一个与auto参数顺序相对应的模板形参,效果与显式的模板参数类似

int main()

{

    cout << "---begin---" << endl;

    auto p = [](auto a, auto b)

    {

        return a + b;

    };

    cout << p(1, 2) << endl;

    cout << p(string("1"), string("2")) << endl;

 

    auto p2 = []<typename T>(T a, T b)

    {

        return a + b;

    };

    cout << p2(1, 2) << endl;

    cout << p2(string("1"), string("2")) << endl;

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

3

12

3

12

---end---

 

  • 每一个lambda都对应了一个不同的类,如果要保持lambda的泛型性进行存储,可以使用C++17中的any,并通过any_cast重新获取对象

int main()

{

    cout << "---begin---" << endl;

    auto ptr1 = [](auto x)

    { cout << x << endl; };

    auto ptr2 = [](auto x)

    { cout << x + 1 << endl; };

    vector<any> v = {ptr1, ptr2};

    any_cast<decltype(ptr1)>(v[0])(1);

    any_cast<decltype(ptr2)>(v[1])(2);

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

1

3

---end---

 

  • lambda可以通过转换函数变为函数指针,而泛型lambda转化为函数指针时将无法保持泛型性,但指定类型后仍可以正常转化

int main()

{

    cout << "---begin---" << endl;

    auto ptr1 = [](auto x)

    { cout << x << endl; };

    // void (*p0)(auto x) = ptr1; /*error*/

    void (*p)(int x) = ptr1;

    (*p)(1);

    cout << "---end---" << endl;

    return 0;

}

输出结果:

---begin---

1

---end---

 

  • 可以显示指定lambda为constexpr,但如果没有指定constexpr,而能满足constexpr的所有要求,那么它也将是constexpr的

 

from: https://blog.csdn.net/weixin_62712365/article/details/128742582

 

标签:decltype,进阶,constexpr,int,auto,---,关键字,lambda,cout
From: https://www.cnblogs.com/im18620660608/p/17409340.html

相关文章

  • ShardingSphere + Pagehelper 组合sql查询中包含 DISTINCT GROUP BY 等关键字和聚合函
    Pagehelper中配置说明params:为了支持startPage(Objectparams)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值,可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值,默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;r......
  • linux grep 查找文件中关键字
    grep[选项]{关键字}文件名选项:-i:忽略大小写-n:输出关键字行号-v:取反,不输出包含关键字内容-r:递归查找,用于查找多个文件是否包含某个关键字-E:使用正则表达式查找file.txt文件中匹配关键字的-所有行grep"关键字1"file.txt查找file.txt文件中匹配多个关键字......
  • Django4全栈进阶之路24 项目实战(报修类型表):CKEditor富文本
    CKEditor是一个强大的富文本编辑器,可以用于在网站或应用程序中创建和编辑内容。以下是在安装和使用CKEditor的一般步骤:安装CKEditor:下载CKEditor:访问CKEditor官方网站(https://ckeditor.com/)并下载适用于您的项目的CKEditor版本。解压文件:将下载的CKEditor压缩包解压到您的项目......
  • AutoMagic设计思路简介及新增自定义关键字实例
    目录  简介  AutoMagic介绍  SeleniumKey介绍  自定义关键字简介AutoMagic是一个基于WebUI的自动化管理平台。为什么叫AutoMagic呢?因为自动化在执行起来的时候是一个很神奇的事情,它可以无人值守的模拟人的操作,就像魔术(Magic)一样。所以我给她取名叫AutoMagic。Aut......
  • Java:static关键字
    (一)static关键字的基本用法1.static关键字基本概念我们可以一句话来概括:方便在没有创建对象的情况下来进行调用。也就是说:被static关键字修饰的不需要创建对象去调用,直接根据类名就可以去访问。2.static关键字修饰类Java里面static一般用来修饰成员变量或函数。但有一种特殊用......
  • buuctf [第二章 web进阶]XSS闯关
    本题每一关都需要我们使用alert弹窗level1URL为http://7db5b895-7c64-4b97-a85e-bc011762312f.node4.buuoj.cn:81/level1?username=xss查看源码可知get传的username直接被输出所以直接注入js代码即可?username=<script>alert(1)</script>level2level2对输入的username......
  • C++ 图进阶系列之剖析二分图的染色算法和匈牙利算法
    1.前言二分图又称作二部图或称为偶图,是图论中的一种特殊类型,有广泛的应用场景。什么是二分图?二分图一般指无向图。看待问题要有哲学思想,有二分图也可以是有向图。如果图中所有顶点集合能分成两个独立的子集,且任一子集中的任意顶点之间没有边连接,则称这样的图为二分图。......
  • buuctf [第二章 web进阶]SSRF Training
    首先点击interstingchallenge,查看后台源码。可以看到是将输入的ip通过safe_request_url()调用check_inner_ip()来判断是不是内网ip。如果是内网ip,那么直接输出;如果不是,则会创建一个curl会话,并向目标url发起请求,将返回结果输出。根据主页提示,flag位于flag.php中,但是如果直接输......
  • 指针进阶(3)————玩转指针
    指针进阶内容不多,但面面俱到,都是精华1.回调函数:2.详解qsort函数参数:3.模拟实现qsort函数回调函数就是,把一个函数的地址,放在函数指针中,然后将该指针作为一个参数,传到另一个函数中,在这个函数内部使用了外部写好的一个函数.举一个例子,看完你一定明白了例子:voidmenu(void){ print......
  • 40+JavaScript进阶单行代码
    数组//生成数组0-99的数组//方案1constcreateArr=(n)=>Array.from(newArray(n),(v,i)=>i);letarr=createArr(100);console.log(arr);//方案2constcreateArr=(n)=>newArray(n).fill(0).map((v,i)=>i)......