文章目录一、从C11引入1. 常量表达式和constexpr关键字的概念2. constexpr修饰函数二、constexpr在C14中的进化三、constexpr在C17中的进化四、constexpr在C20中的进化五、C20的consteval和constinit一、从C11引入1. 常量表达式和constexpr关键字的概念现代C从C11开始引入了常量表达式和constexpr关键字的概念并且在之后的C标准中不断更新常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式。用字面量、常量表达式初始化的const对象都是常量表达式。但是用变量初始化的const对象不是常量表达式。constinta1;//a是常量表达式constintba1;//b是常量表达式intc1;//c不是常量表达式constintdc;//d不是常量表达式因为可能有代码影响c的值编译时期无法确定c的值由于常量表达式的值在编译时期就能确定编译器在这里就能进行优化在使用常量表达式的地方直接进行替换值通过汇编语言我们能看出区别常量表达式的最大优点就是能将一些计算从运行时转移到编译时提升程序运行时的性能。C11引入了一个关键字constexpr用于指定常量表达式它允许在编译时计算表达式的值。constexpr可以修饰变量但修饰的变量一定要是常量表达式且必须用常量或常量表达式进行初始化否则会报错constexpr也可以修饰指针但修饰的是指针本身也是就顶层const。const可以分为顶层const、底层const。本身被const修饰叫做顶层const、指向的对象被const修饰叫做底层const。大多数普通对象被const修饰时都是顶层const指针被const修饰时*左边的const叫做底层const*右边的const叫做顶层constconst修饰引用时是底层const2. constexpr修饰函数constexpr可以修饰函数constexpr函数默认都是inline的函数的计算会由编译器在编译时期执行。但是一个函数的逻辑可比表达式的逻辑复杂多了这对于编译器的要求就更加严格因此在C11的标准中对constexpr修饰的函数要求也十分严格这个函数的参数和返回值必须都是字面类型目前可以理解为包括整型、浮点型、指针、引用等函数体中只能包含一条语句且是return语句函数体中不能定义局部变量不能有循环语句、条件语句函数必须有返回值返回值必须是常量表达式。函数如果有参数必须传常量或常量表达式不这样传参要用普通变量接收函数结果此时这个函数还是普通函数的运行执行constexpr函数的返回结果必须用一个常量表达式进行接收才能触发编译器的优化。否则这个函数还是像普通函数一样在运行调用时才会计算constexprintfunc1(){return1;}constexprintfunc2(intx){returnx10;}constexprintfunc3(intn){returnn1?1:n*func3(n-1);}intmain(){constexprintafunc1();constexprintbfunc2(10);constexprintcfunc3(5);coutaendl;coutbendl;coutcendl;return0;}通过汇编语句观察确实在编译时期就计算出了a、b、c的结果在cout语句中直接进行了值替换constexpr还能修饰其他函数constexpr修饰构造函数一个类的构造函数被constexpr修饰了才能定义constexpr修饰的类对象。要求该类的所有成员变量必须是字面类型constexpr构造函数必须在初始化列表初始化所有成员变量必须用常量表达式初始化成员变量函数体必须为空该类的析构函数不能做任何实际清理工作constexpr修饰成员函数这个函数自动成为const成员函数参数列表()后要加上const函数内不能修改成员变量constexpr成员函数不能是虚函数其他要求跟普通函数一样constexpr修饰函数模板如果constexpr修饰的函数模板的实例化结果不符合constexpr普通函数的要求则constexpr会自动忽略这个函数就相当于一个普通函数能看出来C11对于constexpr修饰函数还是相当相当严格的但这些规定会在后面的C标准中逐步放开时代还是在进步的~二、constexpr在C14中的进化C14中大幅放宽了对constexpr函数的限制使其语法和功能更接近普通函数允许了constexpr函数内部声明和初始化局部变量支持函数内的if语句、for/while语句、switch语句等函数内可以有多条return语句函数的返回类型可以是void、自定义类型、STL的std::array容器、等更多复合类型实验时要确保编译器支持的是C14标准constexprintfunc(intn){if(n1||n2){returnn;}intret1;//允许局部变量for(inti2;in;i){ret*i;}returnret;//允许多条return语句}intmain(){constexprintafunc(5);coutaendl;return0;}三、constexpr在C17中的进化C17进一步对constexpr扩展使其能力大幅提升进一步模糊了编译时和运行时的界限。其中constexpr可以修饰lambda表达式了要求lambda表达式的捕获必须是常量表达式函数体满足constexpr函数的要求同样实验时保证编译器是C17标准的intmain(){constexprinta10;constexprautofunc[a](intx)constexpr{returnx*a;};constexprintbfunc(5);coutbendl;return0;}四、constexpr在C20中的进化C20是C的最重要的更新之一也同样对constexpr特性进行了革命使编译器的计算能力得到前所未有的提升使其成为现代C元编程和性能优化的核心工具主要更新了以下内容支持了在编译期动态内存分配C20支持constexpr函数中使用动态内存分配new/delete但所分配的内存必须在编译器被释放。这一用法使得STL库的其他容器的编译期实现成为“可能”但C20还无法在constexpr函数内真正使用vector、string等容器涉及内存的操作继续期待未来的更新吧。支持了constexpr函数内的try-catch语句但是实际不能真正抛出异常。这里主要用于模板约束和编译期的错误检测异常必须在编译期捕获和处理不能传播到运行时期。支持了constexpr可变mutable类成员变量原来的constexpr成员函数中不能修改成员变量但C20后在定义成员变量时加上mutable关键字这个成员变量就能在constexpr成员函数中被修改了。支持了constexpr虚函数C20后开始支持虚函数定义为constexpr函数也就是支持了编译器的多态调用。C23是目前最新的C标准但编译器支持仍在路上主要因为标准本身的复杂性、厂商对稳定性的优先考量以及实现策略的差异。让我们期待C的未来无限发展可能吧五、C20的consteval和constinitconstexpr的核心本质是允许在编译器进行计算。但如果上下文要求它在运行时计算constexpr也可以像一个普通函数一样运行。为了杜绝这种不确定性C20引入了consteval这个关键字他比constexpr更严格。consteval修饰的函数必须在编译时被调用和求值如果一个consteval函数不能在编译时被调用和求值那么程序就会编译报错。同时C20另一个关键字constinit用于修饰具有静态存储期或线程存储期的变量如全局变量static变量不能修饰局部变量。它的作用是强制该变量在编译期初始化完成。适合用在需要确保编译时初始化但不要求变量保持常量的场景中。本篇完感谢阅读