C++ const、constexpr、consteval关键字详解与对比

📅 发布时间:2026/8/5 11:32:03
C++ const、constexpr、consteval关键字详解与对比 C const、constexpr、consteval关键字详解与对比一、const、constexpr、consteval关键字详解与对比1、 引言2、 const 关键字2.1 、基本用法2.2、 核心特性3、 constexpr 关键字 (C11 引入)3.1、 修饰变量3.2、 修饰函数3.3 、核心特性4、 consteval 关键字 (C20 引入)4.1、 基本用法4.2、 核心特性5、三者的详细对比6、 如何选择7、 总结二、代码示例一、const、constexpr、consteval关键字详解与对比1、 引言在 C 编程中const、constexpr和consteval是三个用于定义“常量”或“常量表达式”的关键字它们各自有不同的语义、应用场景和编译期要求。理解它们的区别对于编写高效、安全且符合现代 C 风格的代码至关重要。2、 const 关键字const常量是 C 中最基础、最常用的常量限定符。它的核心作用是声明一个“运行时常量”即该对象的值在初始化后不可修改。2.1 、基本用法// 1. 修饰变量constintmax_size100;// max_size 200; // 错误不能修改 const 变量// 2. 修饰指针intvalue10;constint*ptr1value;// 指向常量的指针指针可变指向的值不可变int*constptr2value;// 常量指针指针不可变指向的值可变constint*constptr3value;// 指向常量的常量指针都不可变// 3. 修饰成员函数classMyClass{public:intgetValue()const{// 承诺不修改成员变量returndata;}private:intdata42;};2.2、 核心特性运行时常量const变量的值在运行时确定并固定。它可以是编译期已知的值也可以是运行时计算的结果。类型安全防止意外修改增强代码可读性和安全性。链接性在文件作用域内const变量默认具有内部链接C中除非显式声明为extern。3、 constexpr 关键字 (C11 引入)constexpr常量表达式在 C11 中引入用于声明变量或函数可以在编译时求值。它的目标是允许更多的计算在编译期完成提升运行时性能。3.1、 修饰变量constexpr变量必须是编译期常量。constexprintarray_size10;// 编译期常量intmy_array[array_size];// 合法数组大小需编译期已知constexprdoublepi3.1415926;// constexpr int runtime_var std::rand(); // 错误std::rand() 是运行时函数3.2、 修饰函数constexpr函数如果传入编译期常量参数则可以在编译期求值如果传入运行时参数则退化为普通函数在运行时求值。constexprintsquare(intx){returnx*x;}intmain(){constexprintval1square(5);// 编译期计算val1 是编译期常量intruntime_arg10;intval2square(runtime_arg);// 运行时计算// constexpr int val3 square(runtime_arg); // 错误runtime_arg 不是编译期常量return0;}3.3 、核心特性编译期求值核心目标是启用编译期计算。条件编译期constexpr函数是“可能”在编译期执行的函数具体取决于调用上下文。C14/17/20 的增强后续标准大幅放宽了constexpr函数体内的限制如允许循环、局部变量等使其能表达更复杂的逻辑。4、 consteval 关键字 (C20 引入)consteval立即函数在 C20 中引入用于声明函数必须在编译期求值。它创建了一个“立即函数”任何调用它的表达式都必须是常量表达式。4.1、 基本用法constevalintadd(inta,intb){returnab;}intmain(){constexprintresultadd(2,3);// 正确编译期计算// int x 5;// int result2 add(x, 3); // 错误x 不是常量表达式导致 add 无法在编译期调用return0;}4.2、 核心特性强制编译期consteval函数的所有调用都必须在编译期完成否则编译错误。更严格的保证提供了比constexpr更强的编译期计算保证适用于那些必须、且总是希望在编译期完成的计算。不能用于运行时与constexpr不同consteval函数不能处理运行时参数。5、三者的详细对比特性constconstexpr(C11)consteval(C20)主要目的声明运行时常量防止修改声明编译期可求值的变量或函数声明必须在编译期求值的函数求值时机运行时初始化值可编译期或运行时确定可能在编译期求值取决于上下文必须在编译期求值修饰变量✔️ 运行时常量✔️ 编译期常量❌ 不能直接修饰变量修饰函数✔️ 成员函数承诺不修改对象状态✔️ 函数可能编译期求值✔️ 函数必须编译期求值立即函数编译期保证无条件性可能编译期可能运行时绝对性总是编译期典型应用场景保护数据不被意外修改、接口常量、成员函数限定编译期计算、模板元编程、需要编译期常量的上下文如数组大小强制编译期计算、编译期反射、确保某些逻辑无运行时开销6、 如何选择遵循以下决策流程你需要一个常量值吗是→ 进入步骤2。否→ 使用普通变量/函数。这个值是否必须在编译期就知道例如用于数组大小、模板参数、case标签是→ 使用constexpr变量。否→ 使用const变量它也可以是编译期已知的但不强制。你需要一个函数吗是→ 进入步骤4。否→ 结束。你是否希望/允许这个函数在运行时被调用是且希望它也能处理编译期常量→ 使用constexpr函数。否必须强制在编译期执行→ 使用consteval函数C20。仅用于运行时且不修改成员状态→ 使用const成员函数。简单记忆const “这个值/对象不变。”constexpr “这个值/函数可以在编译期算出来。”consteval “这个函数必须在编译期算出来。”7、 总结const、constexpr和consteval构成了 C 常量体系的三个层次从基础的“不变性”到强制的“编译期计算”为开发者提供了不同级别的控制与优化能力。const是基石保障不变性与安全。constexpr是桥梁开启编译期计算的大门平衡灵活性与性能。consteval是利刃在 C20 中提供最强的编译期保证用于需要绝对编译期执行的场景。在实际开发中应根据具体需求选择合适的关键字。通常优先考虑constexpr以获得潜在的编译期优化在需要明确强制编译期行为时使用consteval而const则继续在其传统的“只读”领域发挥重要作用。二、代码示例#includeiostream#includestring// 1. const 演示运行时常量仅只读 voiddemo_const(){// 1. const 修饰变量运行时只读值可运行时确定intruntime_val100;constintc_aruntime_val;// OKconst允许运行时值// c_a 200; // 编译报错const变量不可修改// const 修饰指针intx1;constint*p1x;// 指针指向const数据*p1不可改int*constp2x;// 指针本身constp2不能指向别的地址// const 修饰函数仅保证不修改成员类内演示在后}// 2. constexpr 演示编译期/运行期双兼容 // constexpr 普通函数既能编译期调用也能运行时调用constexprintadd(inta,intb){returnab;}// constexpr 结构体构造字面类型可编译期创建对象structVec2{floatx,y;// constexpr构造函数constexprVec2(floatx_0,floaty_0):x(x_),y(y_){}// constexpr 成员函数constexprfloatlength_sq()const{returnx*xy*y;}};// 模板 constexprtemplatetypenameTconstexprTsquare(T v){returnv*v;}// 3. consteval 演示C20 强制仅编译期调用 // consteval 函数只能在编译期求值运行时直接编译报错constevalintcompile_only_mul(inta,intb){returna*b;}// consteval 构造函数仅允许编译期实例化structConstEvalPoint{intu,v;constevalConstEvalPoint(inta,intb):u(a),v(b){}};// 4. 常量表达式模板参数 C20 templateVec2 VstructVecWrapper{staticconstexprfloatlen2V.length_sq();};intmain(){// ---------------- const 测试 ----------------demo_const();intnum50;constintrun_constnum;// const 接受运行时变量// ---------------- constexpr 变量编译期常量 ----------------constexprintc1add(1,2);// 编译期计算intruntime_in10;intc2add(runtime_in,5);// constexpr函数运行时调用也合法constexprVec2v_compile(3,4);// 编译期创建结构体constexprfloatlenv_compile.length_sq();constexprintsqsquare(7);std::coutconstexpr编译期值: c1 len sq\n;// 常量作为模板参数C20constexprfloatwrapper_lenVecWrapperVec2(2,3)::len2;std::cout模板常量参数长度平方: wrapper_len\n;// ---------------- consteval 编译期调用合法 ----------------constexprintce_valcompile_only_mul(6,7);std::coutconsteval编译乘法: ce_val\n;constexprConstEvalPointpt(10,20);// 合法编译期构造// ---------------- 边界错误示例注释打开会编译失败用于理解限制 ----------------// 1. consteval 不能运行时调用// int bad compile_only_mul(runtime_in, 2);// 2. consteval构造不能运行时创建// ConstEvalPoint bad_pt(runtime_in, 1);// 3. constexpr变量不能接收运行时值// constexpr int bad_constexpr runtime_in;// 4. const 无法用于编译期上下文模板/constexpr变量// constexpr int bad run_const;return0;}