【C++】现代C++的新特性constexpr,及其在C++14、C++17、C++20中的进化

【C++】现代C++的新特性constexpr,及其在C++14、C++17、C++20中的进化

各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。
如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步!
也欢迎关注我的blog主页:
落羽的落羽

文章目录

  • 一、从C++11引入
    • 1. 常量表达式和constexpr关键字的概念
    • 2. constexpr修饰函数
  • 二、constexpr在C++14中的进化
  • 三、constexpr在C++17中的进化
  • 四、constexpr在C++20中的进化

一、从C++11引入

1. 常量表达式和constexpr关键字的概念

现代C++,从C++11开始,引入了常量表达式和constexpr关键字的概念,并且在之后的C++标准中不断更新

常量表达式是指,值不会改变并且在编译过程中就能得到计算结果的表达式。用字面量、常量表达式初始化的const对象都是常量表达式。但是用变量初始化的const对象不是常量表达式。

constint a =1;//a是常量表达式constint b = a +1;//b是常量表达式int c =1;//c不是常量表达式constint d = c;//d不是常量表达式,因为可能有代码影响c的值,编译时期无法确定c的值

由于常量表达式的值在编译时期就能确定,编译器在这里就能进行优化,在使用常量表达式的地方直接进行替换值,通过汇编语言我们能看出区别:

在这里插入图片描述

常量表达式的最大优点就是能将一些计算从运行时转移到编译时,提升程序运行时的性能。

C++11引入了一个关键字:constexpr,用于指定常量表达式,它允许在编译时计算表达式的值。
constexpr可以修饰变量,但修饰的变量一定要是常量表达式,且必须用常量或常量表达式进行初始化,否则会报错

在这里插入图片描述

constexpr也可以修饰指针,但修饰的是指针本身,也是就顶层const。

const可以分为顶层const、底层const。本身被const修饰叫做顶层const、指向的对象被const修饰叫做底层const。大多数普通对象被const修饰时都是顶层const;指针被const修饰时,*左边的const叫做底层const,*右边的const叫做顶层const;const修饰引用时,是底层const

2. constexpr修饰函数

constexpr可以修饰函数,constexpr函数默认都是inline的,函数的计算会由编译器在编译时期执行。但是,一个函数的逻辑可比表达式的逻辑复杂多了,这对于编译器的要求就更加严格,因此在C++11的标准中,对constexpr修饰的函数要求也十分严格:

  • 这个函数的参数和返回值必须都是字面类型(目前可以理解为包括整型、浮点型、指针、引用等)
  • 函数体中只能包含一条语句且是return语句
  • 函数体中不能定义局部变量,不能有循环语句、条件语句
  • 函数必须有返回值,返回值必须是常量表达式。函数如果有参数,必须传常量或常量表达式(不这样传参,要用普通变量接收函数结果,此时这个函数还是普通函数的运行执行)

constexpr函数的返回结果,必须用一个常量表达式进行接收,才能触发编译器的优化。否则这个函数还是像普通函数一样,在运行调用时才会计算:

constexprintfunc1(){return1;}constexprintfunc2(int x){return x +10;}constexprintfunc3(int n){return n <=1?1: n *func3(n -1);}intmain(){constexprint a =func1();constexprint b =func2(10);constexprint c =func3(5); cout << a << endl; cout << b << endl; cout << c << endl;return0;}

通过汇编语句观察,确实在编译时期就计算出了a、b、c的结果,在cout语句中直接进行了值替换

在这里插入图片描述

constexpr还能修饰其他函数:

  • constexpr修饰构造函数,一个类的构造函数被constexpr修饰了,才能定义constexpr修饰的类对象。要求:
    • 该类的所有成员变量必须是字面类型
    • constexpr构造函数必须在初始化列表初始化所有成员变量,必须用常量表达式初始化成员变量,函数体必须为空
    • 该类的析构函数不能做任何实际清理工作
  • constexpr修饰成员函数:
    • 这个函数自动成为const成员函数,参数列表()后要加上const,函数内不能修改成员变量
    • constexpr成员函数不能是虚函数
    • 其他要求跟普通函数一样
  • constexpr修饰函数模板:
    • 如果constexpr修饰的函数模板的实例化结果不符合constexpr普通函数的要求,则constexpr会自动忽略,这个函数就相当于一个普通函数

能看出来,C++11对于constexpr修饰函数还是相当相当严格的,但这些规定会在后面的C++标准中逐步放开,时代还是在进步的~

二、constexpr在C++14中的进化

C++14中,大幅放宽了对constexpr函数的限制,使其语法和功能更接近普通函数:

  • 允许了constexpr函数内部声明和初始化局部变量
  • 支持函数内的if语句、for/while语句、switch语句等
  • 函数内可以有多条return语句
  • 函数的返回类型可以是void、自定义类型、STL的std::array容器、等更多复合类型

实验时,要确保编译器支持的是C++14标准:

在这里插入图片描述
constexprintfunc(int n){if(n ==1|| n ==2){return n;}int ret =1;//允许局部变量for(int i =2; i <= n; i++){ ret *= i;}return ret;//允许多条return语句}intmain(){constexprint a =func(5); cout << a << endl;return0;}
在这里插入图片描述

三、constexpr在C++17中的进化

C++17进一步对constexpr扩展,使其能力大幅提升,进一步模糊了编译时和运行时的界限。

其中,constexpr可以修饰lambda表达式了,要求:

  • lambda表达式的捕获必须是常量表达式
  • 函数体满足constexpr函数的要求

同样,实验时保证编译器是C++17标准的:

在这里插入图片描述
intmain(){constexprint a =10;constexprauto func =[a](int x)constexpr{return x * a;};constexprint b =func(5); cout << b << endl;return0;}
在这里插入图片描述

四、constexpr在C++20中的进化

C++20是C++的最重要的更新之一,也同样对constexpr特性进行了革命,使编译器的计算能力得到前所未有的提升,使其成为现代C++元编程和性能优化的核心工具,主要更新了以下内容:

  • 支持了在编译期动态内存分配:C++20支持constexpr函数中使用动态内存分配(new/delete),但所分配的内存必须在编译器被释放。这一用法使得STL库的其他容器的编译期实现成为“可能”,但C++20还无法在constexpr函数内真正使用vector、string等容器涉及内存的操作,继续期待未来的更新吧。
  • 支持了constexpr函数内的try-catch语句,但是实际不能真正抛出异常。这里主要用于模板约束和编译期的错误检测,异常必须在编译期捕获和处理,不能传播到运行时期。
  • 支持了constexpr可变(mutable)类成员变量:原来的constexpr成员函数中,不能修改成员变量,但C++20后在定义成员变量时加上mutable关键字,这个成员变量就能在constexpr成员函数中被修改了。
  • 支持了constexpr虚函数:C++20后开始支持虚函数定义为constexpr函数,也就是支持了编译器的多态调用。

C++23是目前最新的C++标准,但编译器支持仍在路上,主要因为标准本身的复杂性、厂商对稳定性的优先考量以及实现策略的差异。让我们期待C++的未来无限发展可能吧!

本篇完,感谢阅读

Read more

优选算法——位运算

👇作者其它专栏 《数据结构与算法》《算法》《C++起始之路》 1.前要知识 《位操作符的妙用》 2.相关题解 2.1判定字符是否唯一 算法思路: 利用【位图】的思想,每一个【比特位】代表一个【字符】,一个int类型的变量的32位足够表示所有的小写字母。比特位里若为0,表示这个字符没有出现过;若为1,表示该字符出现过。 可以用一个【整数】来充当【哈希表】。 class Solution { public: bool isUnique(string astr) { //利用鸽巢原理优化 if(astr.size()>26) return false; int bitmap=0; for(auto i:

By Ne0inhk

算法优化:提升Lite-Avatar实时性的关键数据结构

算法优化:提升Lite-Avatar实时性的关键数据结构 1. 引言 实时数字人交互正成为AI应用的新趋势,但要做到真正的实时响应并不容易。想象一下,当你和数字人对话时,如果它的嘴型总是慢半拍,或者表情跟不上你的语音节奏,那种体验会有多糟糕。Lite-Avatar作为一款轻量级实时数字人引擎,面临着如何在有限计算资源下实现毫秒级响应的挑战。 传统的数字人渲染往往需要大量的计算资源,导致延迟较高,难以满足实时交互的需求。Lite-Avatar通过精心设计的数据结构和算法优化,成功将渲染延迟控制在50毫秒以内,这背后有着怎样的技术奥秘?本文将带你深入探索那些让Lite-Avatar如此高效的关键数据结构。 2. 实时性挑战与优化思路 2.1 数字人渲染的实时性要求 实时数字人交互对延迟有着极其严格的要求。根据人类感知研究,当延迟超过100毫秒时,用户就能明显感觉到音画不同步。而要实现自然的对话体验,延迟需要控制在50毫秒以内。这意味着从音频输入到画面输出的整个处理链路必须在极短时间内完成。 Lite-Avatar面临的挑战包括:音频特征提取、表情预测、口型同步、画面渲染等

By Ne0inhk

优选算法——二分查找

👇作者其它专栏 《数据结构与算法》《算法》《C++起始之路》 二分查找相关题解 1.二分查找 算法思路: a.定义left,right指针,分别指向数组的左右区间。 b.找到待查找区间的中间点mid,找到后分三种情况讨论:         i.arr[mid]==target说明正好找到,返回mid的值;         ii.arr[mid]>target说明[mid,right]这段区间都是大于target的,因此舍去右边区间,在左边[left,mid-1]的区间继续查找,即让right=mid-1,然后重复b过程;         iii.arr[mid]<target说明[left,mid]这段区间的值都是小于target的,因此舍去左边区间,在右边区间[mid+1,right]

By Ne0inhk
【算法通关指南:算法基础篇】二分算法:1.在排序树组中查找元素的第一个和最后一个位置 2.牛可乐和魔法封印

【算法通关指南:算法基础篇】二分算法:1.在排序树组中查找元素的第一个和最后一个位置 2.牛可乐和魔法封印

🔥小龙报:个人主页 🎬作者简介:C++研发,嵌入式,机器人方向学习者 ❄️个人专栏:《算法通关指南》 ✨ 永远相信美好的事情即将发生 文章目录 * 前言 * 一、二分算法 * 二、在排序树组中查找元素的第一个和最后一个位置 * 2.1题目 * 2.2 算法原理 * 2.3代码 * 三、牛可乐和魔法封印 * 3.1题目 * 3.2 算法原理 * 3.3代码 * 总结与每日励志 前言 本专栏聚焦算法题实战,系统讲解算法模块:以《c++编程》,《数据结构和算法》《基础算法》《算法实战》 等几个板块以题带点,讲解思路与代码实现,帮助大家快速提升代码能力ps:本章节题目分两部分,比较基础笔者只附上代码供大家参考,其他的笔者会附上自己的思考和讲解,希望和大家一起努力见证自己的算法成长 一、

By Ne0inhk