3.18 constexpr函数

3.18 constexpr函数

constexpr函数是C++11及以后标准的核心特性,核心作用是让函数能在「编译阶段」计算出结果(而非运行阶段),从而提升程序性能、增强代码安全性,还能用于要求"编译期常量"的场景。

一、先搞懂核心概念:编译期计算 vs 运行期计算

  • 普通函数 :调用时在程序「运行阶段」计算结果(比如运行时才执行add(3,5)得到8);
  • constexpr函数 :满足条件时,编译器会在「编译阶段」直接算出结果(比如编译时就把add(3,5)替换成8),运行时无需计算。

可以把constexpr函数想象成"编译器能执行的函数"------只要输入是编译期常量,结果就提前算好,运行时直接用结果,省掉计算开销。

二、constexpr函数的核心用途(按重要性排序)

  1. 提升程序性能:编译期计算,避免运行时开销

对于需要反复调用、逻辑简单的函数(比如数学计算、常量推导),constexpr能把计算提前到编译阶段,运行时直接用结果,尤其适合高频调用的场景。

示例:普通函数 vs constexpr函数

cpp 复制代码
#include <iostream>
using namespace std;

// 普通函数:运行时计算
int add(int a, int b) {
    return a + b;
}

// constexpr函数:编译期计算(输入为常量时)
constexpr int constexpr_add(int a, int b) {
    return a + b;
}

int main() {
    // 1. 普通函数:运行时计算3+5
    int res1 = add(3, 5); 
    // 2. constexpr函数:编译时就算出8,运行时直接赋值
    constexpr int res2 = constexpr_add(3, 5); 
    cout << res1 << " " << res2 << endl; // 输出8 8
    return 0;
}
  • 反编译能看到:res2的赋值直接是8,没有函数调用指令;res1则有函数调用的指令。
  1. 用于"编译期常量场景"(必须用编译期值的地方)

C++中有些场景要求"值必须是编译期常量"(比如数组大小、模板参数、枚举值),普通函数的返回值无法满足,但constexpr函数可以。

示例:用constexpr函数定义数组大小

cpp 复制代码
#include <iostream>
using namespace std;

// 计算数组大小的constexpr函数
constexpr int get_arr_size(int n) {
    return n * 2;
}

int main() {
    // 1. 错误:普通函数的返回值是运行时的,不能定义数组大小
    // int size = add(3,2);
    // int arr1[size]; // 编译报错(非编译期常量)

    // 2. 正确:constexpr函数返回编译期常量,可定义数组大小
    constexpr int arr_size = get_arr_size(3); // 编译期算出6
    int arr2[arr_size] = {1,2,3,4,5,6}; // 合法,数组大小是6
    cout << "数组大小:" << sizeof(arr2)/sizeof(int) << endl; // 输出6
    return 0;
}
  1. 增强代码安全性:编译期检查错误

如果constexpr函数的输入是常量,但逻辑有问题(比如除以0),编译器会直接报错,而不是运行时崩溃,能提前发现问题。

示例:编译期检查错误

cpp 复制代码
constexpr int div(int a, int b) {
    return a / b; // 除法逻辑
}

int main() {
    // 编译时报错:division by zero(除以0)
    // constexpr int res = div(10, 0); 
    return 0;
}
  • 普通函数调用div(10,0)会在运行时崩溃,而constexpr函数直接在编译期暴露问题。
  1. 兼容运行时调用:灵活度高

constexpr函数并非"只能编译期执行"------如果输入是运行时变量(比如用户输入的值),它会退化成普通函数,在运行时计算,不影响使用。

示例:兼容运行时调用

cpp 复制代码
constexpr int mul(int a, int b) {
    return a * b;
}

int main() {
    int x, y;
    cin >> x >> y; // 运行时输入(变量)
    // 此时mul退化成普通函数,运行时计算x*y
    int res = mul(x, y); 
    cout << res << endl;
    return 0;
}

三、constexpr函数的使用条件(关键限制)

要让constexpr函数能在编译期计算,必须满足:

  1. 函数体足够简单(C++11只允许1条return语句;C++14及以后支持循环、分支等);
  2. 输入参数必须是编译期常量(否则退化成运行时计算);
  3. 返回值必须能在编译期确定。

总结

核心用途 具体价值
编译期计算 提升性能,避免运行时重复计算
支持编译期常量场景 可用于数组大小、模板参数等要求"编译期值"的地方
编译期错误检查 提前发现逻辑错误(如除以0),增强代码安全性
兼容运行时调用 输入是变量时退化成普通函数,灵活度不损失

简单来说,constexpr函数的核心是"把能提前算的都在编译期算完"------既提升性能,又保证安全,是C++中优化常量计算的核心工具。

3.18.1 constexpr与const的区别

constexprconst的核心区别------这两个关键词都和"不可修改"有关,但作用场景、生效时机完全不同:

一、核心区别总览

对比维度 const constexpr
核心语义 「只读」:变量/对象不能被修改 「编译期常量」:值在编译期确定
生效时机 运行期(修饰变量)/编译期(修饰常量) 编译期(函数/变量均在编译期确定)
适用对象 变量、函数参数、类成员等 函数、变量、表达式等
能否参与编译期计算 const常量(如const int a=5)可以,const变量(如const int a=x)不行 只要满足条件,必然参与编译期计算

二、分场景详细对比

  1. 修饰变量:const变量 vs constexpr变量
cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int x = 10; // 运行时变量

    // const变量:
    const int a = 5;       // 「编译期常量」:值是字面量,不可修改
    const int b = x;       // 「运行时只读变量」:值是运行时变量,不可修改,但编译期不确定

    // constexpr变量:
    constexpr int c = 5;   // 「编译期常量」:值必须在编译期确定
    // constexpr int d = x; // 编译报错:x是运行时变量,无法在编译期确定值

    // 关键差异:能否用于「编译期常量场景」
    int arr1[a]; // 合法(a是const编译期常量)
    // int arr2[b]; // 编译报错(b是const运行时变量,无法确定数组大小)
    int arr3[c]; // 合法(c是constexpr编译期常量)
    return 0;
}
  • const变量分两种:若初始化值是字面量/编译期常量 ,则是编译期常量;若初始化值是运行时变量,则是"运行时只读变量"(无法参与编译期计算);
  • constexpr变量必须是编译期常量,初始化值必须能在编译期确定,否则直接报错。
  1. 修饰函数:const函数 vs constexpr函数
cpp 复制代码
// const函数(仅用于类成员函数):
class A {
public:
    int val = 5;
    // const成员函数:保证函数内不修改类成员变量(运行期约束)
    int get_val() const {
        // val = 10; // 编译报错:const函数不能修改成员变量
        return val;
    }
};

// constexpr函数:
// 编译期计算的函数(输入是编译期常量时,结果在编译期确定)
constexpr int add(int a, int b) {
    return a + b;
}

int main() {
    A obj;
    cout << obj.get_val() << endl; // 运行时调用const成员函数

    constexpr int res = add(3,5); // 编译期计算出8
    cout << res << endl;
    return 0;
}
  • const函数仅用于类成员函数,作用是"保证函数内不修改类成员"(运行期的只读约束);
  • constexpr函数是"编译期可执行的函数",核心是计算时机提前,和"是否修改数据"无关。
  1. 参与编译期计算的能力
cpp 复制代码
// const的局限性:
const int m = 5;       // 编译期常量,可参与编译期计算
const int n = m + 3;   // 编译期计算出8(m是编译期常量)
int y = 2;
const int p = y + 3;   // 运行时变量(y是运行时输入),无法参与编译期计算

// constexpr的优势:
constexpr int q = 5;
constexpr int r = q + 3; // 编译期计算出8
constexpr int s = add(2,3); // 调用constexpr函数,编译期计算出5
  • const只有"初始化值是编译期常量"时,才能参与编译期计算;
  • constexpr只要满足条件,必然在编译期计算,没有例外。

三、一句话总结区别

  • const是"运行期的只读保护":告诉你"这个东西不能改",但不一定知道它的值是多少;
  • constexpr是"编译期的确定值":告诉你"这个东西的值在编译期就定了,不仅不能改,还能提前用"。

简单来说:

  • 若只想"禁止修改",用const
  • 若想"让值在编译期确定,参与编译期计算",用constexpr
相关推荐
别在内卷了2 小时前
三步搞定:双指针归并法求两个有序数组的中位数(Java 实现)
java·开发语言·学习·算法
txinyu的博客2 小时前
select/poll/epoll
linux·c++
人工干智能2 小时前
python的高级技巧:Pandas中的`iloc[]`和`loc[]`
开发语言·python·pandas
wjs20242 小时前
Chart.js 混合图:深入解析与实战指南
开发语言
bing.shao2 小时前
基于 Go + Ollama 开发智能日志分析工具完整实战
开发语言·后端·golang
n***33352 小时前
C++跨平台开发:挑战、策略与未来
开发语言·c++
n***33352 小时前
C语言轮子大赛:挑战底层,突破极限
c语言·开发语言
D_evil__2 小时前
【Effective Modern C++】第一章 类型推导:1.理解模板类型推导
c++
范纹杉想快点毕业2 小时前
C语言100个经典编程练习题(完整标题+清晰排版)
运维·c语言·单片机·嵌入式硬件·算法