当前位置: 首页 > news >正文

C++ constexpr 修饰符与函数

文章目录

  • 一、constexpr 修饰符
    • 1.1 修饰变量:定义编译期常量
    • 1.2 核心作用
    • 1.3 区别对比
  • 二、constexpr 函数
  • 三、字面值
    • 3.1 字面值类型
    • 3.2 指针和引用
    • 3.3 自定义字面值类型
  • 四、允许递归
  • 五、泛型
    • 5.1 模板函数中的 constexpr
    • 5.2 if constexpr(C++17)

一、constexpr 修饰符

在 C++ 中,constexpr 是一个关键字修饰符,其核心作用是指定对象或函数可以在编译期计算,从而将计算逻辑从运行时提前到编译期,带来性能优化、代码安全性提升等好处,同时在编译时就能发现错误。

换句话说:

  • 如果上下文要求编译期常量(如数组大小、模板参数),constexpr 能保证表达式能在编译期计算;
  • 如果上下文允许运行时计算,constexpr 也能在运行时执行(并非强制仅限编译期)。

1.1 修饰变量:定义编译期常量

constexpr 修饰变量时,要求变量必须在定义时初始化,并且初始化值必须是常量表达式,该值不可修改(类似 const,但更严格)。

#include <iostream>
#include <string>
using namespace std;int some_function() {return 999;
}int main() {constexpr int max_size = 100;  // 编译期确定值,可用于数组大小等编译期上下文int arr[max_size];             // 合法:编译期已知大小// 对比 const: const 变量可能在运行时初始化const int runtime_val = some_function();  // 运行时确定值// constexpr int error = some_function(); // 错误:无法在编译期确定值return 0;
}

特点:

  1. constexpr 变量必须立即初始化;
  2. 初始值必须是编译期可计算的表达式;
  3. constexpr 变量一定是编译期常量,而 const 变量可能是运行时常量
  4. constexpr 在模板元编程、常量数组、编译期分支(if constexpr)中尤为重要。

1.2 核心作用

  1. 性能优化:结果在编译期直接计算并嵌入,减少运行时开销。
  2. 安全性提升:在编译期检查逻辑,例如数组大小是否合法。
  3. 支持元编程:结合模板,可以写出编译期执行的算法,如编译期排序、编译期字符串哈希。

1.3 区别对比

  1. constexpr 不是“只能在编译期执行”,而是“允许在编译期执行”;

  2. 不同标准的演进

    • C++11:constexpr 限制非常严格(函数只能有一个 return)。
    • C++14:放宽限制,允许循环、局部变量、多条语句。
    • C++20:支持 constexpr 动态内存分配、虚函数、try-catch 等更复杂场景。

constexprconst#define 区别

特性constexprconst#define
类型检查无(宏替换)
编译期计算必须可能可能(纯替换)
作用域块作用域块作用域全局宏替换,无作用域
调试支持
内存分配无额外开销可能有
  • constexpr最严格的常量语义(编译期 + 类型安全);
  • const 只保证不可修改,不一定编译期常量;
  • #define 只是预处理器替换,缺乏类型检查和作用域控制。

二、constexpr 函数

constexpr 函数是指可以用于常量表达式的函数

constexpr 函数指的是在编译的时候就能得到其返回值的函数,也就是说编译器将 constexpr 函数直接转换成其返回值。

简单理解就是在编译阶段函数已经执行完成并把运行结果填到了 constexpr 函数所在位置。因此,constexpr 函数都是被隐式地定义为内联函数。

编译器会尽量在编译期执行 constexpr 函数,如果参数是常量表达式,则直接计算出结果并替换;如果参数不是常量表达式,函数会退化为普通函数,运行时求值。

所有 constexpr 函数都会被隐式地视为 inline

要点

  1. C++11 限制:函数中只能包含一个 return 语句;
  2. C++14 起:支持多条语句、局部变量、分支语句、循环等;
  3. C++20 起:支持 try-catchnew/delete 等复杂逻辑;
  4. 返回值必须是字面值类型(算术类型、引用、指针属于字面值类型)
  5. 参数必须是字面值类型(自定义类、IO 库、string 类型不属于字面值类型)
  6. constexpr 函数被隐式地指定为内联函数
  7. 允许递归调用。

测试合法性

#include <iostream>
using namespace std;constexpr int add(int a, int b) {return a + b;
}int main() {// 合法:编译期计算,结果为8constexpr int sum1 = add(3, 5);cout << sum1 << endl;// 合法:运行时计算,结果为8(仍合法),// 印证了constexpr不是"仅编译期执行"的标志,而是"允许编译期执行"的标志int x = 3, y = 5;int sum2 = add(x, y);cout << sum2 << endl;// 不合法:编译期计算,不能传入变量;// constexpr int sum3 = add(x, y); // 错误return 0;
}

constexpr 不是“强制编译期执行”,而是“能编译期执行时就执行”。


三、字面值

字面值类型(Literal Types):编译器在编译期就能完全确定的类型。

在编程中,字面值类型指的是编译期就能确定其值的类型,它们的取值是固定的字面值(如数值、字符串等),且在编译阶段即可被编译器完全知晓。这类类型通常用于模板元编程、常量表达式等场景,能在编译期进行计算或校验,提升程序效率或安全性。

3.1 字面值类型

  1. 算术类型:int, long, float, double, bool
  2. 字符类型:char, wchar_t, char16_t, char32_t
  3. 布尔类型:bool
  4. 指针、引用(指向编译期已知地址的对象,如全局变量、静态变量)
  5. 从 C++11 起:自定义字面值类型(满足 constexpr 构造函数、析构函数的类)。

3.2 指针和引用

指向全局变量(编译期可确定地址)的指针和引用也属于字面值类型。

#include <iostream>
#include <string>
using namespace std;// 全局变量,地址编译期可确定
int global_var = 42;constexpr int* change(int* p) {*p = 12;return p;
}int main() {// 合法:指向全局变量的指针是字面值类型,可用于constexprconstexpr int* ptr_to_global = &global_var;// 合法int* p = change(&global_var);cout << p << endl;           // 输出地址cout << ptr_to_global << endl; // 输出相同地址cout << global_var << endl;  // 输出12cout << *p << endl;          // 输出12return 0;
}

输出:

0x56265707e010
0x56265707e010
12
12

如果指针指向的是局部变量(运行时分配地址),就不是字面值类型。

3.3 自定义字面值类型

#include <iostream>
using namespace std;struct Point {int x, y;constexpr Point(int a, int b) : x(a), y(b) {}// 添加一个constexpr成员函数计算距离平方constexpr int distanceSquared() const {return x * x + y * y;}
};constexpr Point p1(3, 4);  // 编译期常量对象int main() {// 编译期计算距离平方constexpr int distSq = p1.distanceSquared();cout << "Point p1: (" << p1.x << ", " << p1.y << ")" << endl;cout << "Distance squared from origin: " << distSq << endl;// 可以在编译期上下文使用constexpr Point p2(5, 12);constexpr int distSq2 = p2.distanceSquared();cout << "Point p2: (" << p2.x << ", " << p2.y << ")" << endl;cout << "Distance squared from origin: " << distSq2 << endl;// 使用constexpr对象作为数组大小int arr[distSq > 10 ? 10 : 5] = {0}; // 条件编译期表达式return 0;
}

输出:

Point p1: (3, 4)
Distance squared from origin: 25
Point p2: (5, 12)
Distance squared from origin: 169

四、允许递归

constexpr 函数允许递归调用,这使得一些经典递归算法可以在编译期计算。

#include <iostream>
#include <string>
using namespace std;// 常量表达式函数
constexpr int factorial(int n) {return n == 1 ? 1 : n * factorial(n - 1);
}// 常量表达式
constexpr int num = 5;int main() {// 编译期间进行计算并且返回结果cout << factorial(num) << endl; // 输出120cout << factorial(3) << endl;   // 输出6// 实参为变量时,程序运行期间计算并返回结果。int i = 8;// constexpr int result = factorial(i); // 报错;表达式必须含有常量值int result = factorial(i); // 运行时计算cout << result << endl;    // 输出40320return 0;
}

输出:

120
6
40320

注意:

  • 如果传入的是常量表达式(如 5),在编译期计算;
  • 如果传入的是变量(如 x),在运行时计算。

五、泛型

constexpr 在泛型编程中扮演着重要角色,特别是结合模板if constexpr(C++17 引入)时,可以让模板逻辑在编译期分支,从而实现更高效、更安全的代码。

5.1 模板函数中的 constexpr

#include <iostream>
using namespace std;// constexpr 模板函数
template <typename T>
constexpr T add(T a, T b) {return a + b;
}int main() {// 编译期计算constexpr int sum = add(10, 20);cout << "Compile-time sum: " << sum << endl;// 运行时计算double d = add(1.1, 2.2);cout << "Run-time sum: " << d << endl;// 演示编译期计算的能力 - 用于数组大小constexpr int array_size = add(5, 5);int arr[array_size] = {0}; // 数组大小在编译期确定cout << "Array size: " << sizeof(arr)/sizeof(arr[0]) << endl;return 0;
}

输出:

Compile-time sum: 30
Run-time sum: 3.3
Array size: 10

5.2 if constexpr(C++17)

if constexpr 是一种编译期分支,只有满足条件的分支才会被实例化。

#include <iostream>
#include <type_traits>
using namespace std;// 重命名函数以避免与 std::type_info 冲突
template <typename T>
constexpr auto get_type_info() {if constexpr (is_integral<T>::value) {return "Integral";} else {return "Non-integral";}
}int main() {cout << get_type_info<int>() << endl;     // 输出 "Integral"cout << get_type_info<double>() << endl;  // 输出 "Non-integral"return 0;
}

作用:

  1. 结合模板,constexpr 能实现零运行时开销的泛型编程;
  2. if constexpr 让模板元编程逻辑更直观,不必依赖 SFINAE;
  3. 编译期递归、分支、计算,极大地增强了 C++ 的元编程能力。
http://www.dtcms.com/a/398484.html

相关文章:

  • Conda虚拟环境管理完全指南:从创建到跨设备迁移
  • SuperScript:C#脚本编辑器、C#脚本引擎
  • 网站建设项目设计的图片青岛seo服务哪家好
  • 打开网站代码怎么写深圳十大建筑设计院
  • 网站如何制作 优帮云关于学校网站建设的请示
  • 网络协议与层次划分
  • Express入门和实践(一)
  • 从零开始搭建深度学习大厦系列-4.Transformer生成式大语言模型
  • 【MySQL初阶】04-数据表的操作
  • 展会进行时 | Splashtop Inc.(浪桥科技)亮相2025中国国际工业博览会
  • SMTPman,smtp服务器的使用全解析与指南!
  • 39.应用层HTTP协议(二)
  • 网站调用微信js视频深圳微信网站建设公司哪家好
  • 佛山网站建设策划做网站需提供什么资料
  • Swift 实现 DLNA 投屏功能:完整技术解析与实践指南
  • Axios 请求
  • Macbook突然无法开机和充电导致项目无法编译-《时光小铺》的开发意外事故~另寻求维修高手指导
  • 用html5写一个宠物小精灵游戏
  • 客观与主观评估扩散模型在构音障碍语音增强中的应用
  • Xcode 26 error
  • python+django/flask的宠物救助及领养系统javaweb
  • 半导体网站建设装饰公司网页设计
  • 企业网站建设策划书 前言做影视网站需要多大硬盘
  • 电子基石:硬件工程师的器件手册 (十六) - 射频电路:电磁波的有意操控
  • DeepSeek辅助整合精简的DuckDB大数运算插件
  • InnoDB Undo Log机制全解析
  • K8s学习笔记(三) kubectl 常用命令
  • 第二章:Java到Go的思维转变
  • EFR32MG21模组(Zigbee)与STM32单片机通信
  • 部署的git仓库地址变更后更改新地址