C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ 关键字constexpr

C++中关键字constexpr的实现示例

作者:MzKyle

constexpr是C++中用于编译期计算的关键词,它允许在编译期确定值并进行安全检查,从而提高编译效率和代码的健壮性,本文主要介绍了C++中关键字constexpr的实现示例,具有一定的参考价值,感兴趣的可以了解一下

constexpr 是 C++11 引入并在后续标准(C++14/C++17/C++20)中持续增强的关键字,其核心作用是在编译期计算常量或表达式,既保证了编译期的安全性检查,又能消除运行期计算的开销,是实现“零成本抽象”和编译期元编程的关键技术。

一、constexpr 的本质与核心目标

constexpr 的字面意思是“常量表达式(constant expression)”,它的设计目标有两个核心:

  1. 编译期确定值:让变量、函数或对象的取值在编译阶段就能计算完成,避免运行时的冗余计算(如重复的数学运算、常量初始化)。
  2. 强类型安全检查:强制编译器验证“是否满足编译期计算条件”,若存在运行时依赖(如动态输入、未初始化变量),则直接报错,避免潜在的逻辑错误。

从本质上看,constexpr 是对“常量”概念的强化——它不仅要求值“不可修改”,更要求值“可在编译期确定”,这是它与传统 const 最核心的区别。

二、constexpr 变量:编译期初始化的常量

constexpr 最基础的用法是修饰变量,要求变量在编译期完成初始化,且后续不可修改。

1. 变量声明的核心要求

正确示例

// 1. 基本类型:编译期计算 3+2 的结果(值为5)
constexpr int a = 3 + 2;
// 2. 数组:大小由 constexpr 变量确定(编译期已知)
constexpr size_t arr_size = 10;
int arr[arr_size]; // 合法,数组大小编译期确定
// 3. 常量表达式嵌套:依赖其他 constexpr 变量
constexpr int b = a * 4; // 编译期计算 5*4=20

错误示例

int x = 5;
// 错误:初始化表达式依赖运行时变量 x(非常量表达式)
constexpr int c = x + 3; 
// 错误:未初始化(constexpr 变量必须声明时初始化)
constexpr double d; 

2. 修饰指针与引用的特殊规则

constexpr 修饰指针/引用时,需明确“修饰的是指针本身”还是“指向的对象”,规则与 const 类似,但要求更严格(必须编译期确定地址):

三、constexpr 函数:编译期可执行的函数

constexpr 函数是支持“编译期调用”的函数——当函数的实参是常量表达式时,函数会在编译期计算结果;若实参是运行时变量,则退化为普通函数在运行期执行(即“两态性”)。

const修饰的函数(修饰函数返回值或者函数类型)不可以因为constexpr的存才而删除,起到的作用不同,constexpr修饰作用可退回普通函数,但是const修饰的函数必定是常函数。

1. 函数声明的核心要求(随标准演进放宽)

constexpr 函数的限制在 C++11 到 C++20 中逐步放宽,核心要求如下:

标准版本核心限制允许的操作
C++111. 函数体仅允许 return 语句
2. 不能有局部变量/循环
3. 返回值和参数必须是字面类型
仅常量表达式计算、return
C++141. 允许定义局部变量(需是字面类型,可初始化)
2. 允许循环(for/while)
3. 允许条件判断(if/else)
局部变量、循环、条件判断、常量计算
C++171. 允许 constexpr lambda(嵌套使用)
2. 允许使用部分标准库函数(如 std::string_view)
lambda、部分标准库调用
C++201. 允许动态内存分配(new/delete,但需在编译期释放)
2. 允许使用 std::vector/std::string(部分实现)
3. 允许 try-catch(仅编译期异常)
动态内存(编译期释放)、复杂容器、异常处理

2. 典型示例:编译期计算阶乘

// C++14 及以上:constexpr 函数支持循环
constexpr int factorial(int n) {
    if (n < 0) throw "n must be non-negative"; // C++20 允许 try-catch
    int result = 1;
    for (int i = 1; i <= n; ++i) {
        result *= i;
    }
    return result;
}

// 1. 编译期调用:实参是常量表达式(5),结果在编译期计算为 120
constexpr int f5 = factorial(5); 
// 2. 运行期调用:实参是运行时变量(x),结果在运行期计算
int x = 6;
int f6 = factorial(x); 

3. 关键特性:两态性与编译期验证

四、constexpr 类与对象:编译期构造的自定义类型

C++11 允许类通过 constexpr 构造函数 创建“编译期对象”,即对象的所有成员在编译期初始化,且对象的成员函数可通过 constexpr 修饰实现编译期调用。

1. constexpr 构造函数的要求

示例:编译期可构造的 Point 类

class Point {
private:
    int x_, y_;
public:
    // C++11 风格:空函数体,通过初始化列表初始化成员
    constexpr Point(int x, int y) : x_(x), y_(y) {}
    
    // constexpr 成员函数:编译期计算两点距离的平方
    constexpr int distance_sq(const Point& other) const {
        int dx = x_ - other.x_;
        int dy = y_ - other.y_;
        return dx*dx + dy*dy;
    }
    
    // 普通成员函数:仅运行期调用
    void print() const {
        std::cout << "(" << x_ << "," << y_ << ")\n";
    }
};

// 1. 编译期创建对象:构造函数实参是常量表达式
constexpr Point p1(1, 2);
constexpr Point p2(4, 6);
// 2. 编译期调用成员函数:计算距离平方(结果 25)
constexpr int dist_sq = p1.distance_sq(p2); 
// 3. 运行期调用普通成员函数
p1.print(); 

2. constexpr 对象的特性

五、C++17/C++20 关键增强:constexpr lambda、consteval 与 constinit

随着标准演进,constexpr 的能力大幅扩展

1. C++17:constexpr lambda

C++17 允许 lambda 表达式通过 constexpr 修饰(或隐式满足 constexpr 条件),使其可在编译期执行。

示例:编译期使用 lambda 计算数组总和

#include <array>

constexpr auto sum_array = [](const auto& arr) {
    int sum = 0;
    for (auto val : arr) {
        sum += val;
    }
    return sum;
};

// 编译期计算数组总和:sum = 1+2+3+4+5 = 15
constexpr std::array<int, 5> arr = {1,2,3,4,5};
constexpr int total = sum_array(arr); 

2. C++20:consteval 与 constinit

C++20 新增两个关键字,进一步细化“编译期计算”的语义,与 constexpr 形成互补:

constexpr、consteval、constinit 对比

关键字核心语义是否允许运行期执行适用场景
constexpr可编译期执行(两态性)是(实参为运行时变量时)兼顾编译期计算与运行期灵活调用
consteval必须编译期执行(强制)否(否则报错)确保零运行时开销(如编译期哈希)
constinit强制编译期初始化是(变量可后续修改)静态变量的编译期初始化(如全局配置)

六、constexpr 的核心应用场景

constexpr 并非“语法糖”,而是解决实际问题的工具,核心应用场景包括:

1. 编译期计算:消除运行时开销

对于固定逻辑(如数学公式、常量配置),通过 constexpr 在编译期计算结果,避免运行时重复计算。例如:

2. 静态断言(static_assert):编译期合法性检查

static_assert 的条件必须是常量表达式,constexpr 可提供复杂的编译期条件判断,实现更灵活的断言。例如:

// 编译期检查模板参数是否为偶数
template <int N>
void process() {
    static_assert(N % 2 == 0, "N must be even"); // 条件依赖 constexpr 计算
}

process<4>(); // 正确:4 是偶数
// process<5>(); // 错误:编译期断言失败,提示 "N must be even"

3. 编译期元编程:生成代码逻辑

结合模板与 constexpr,可在编译期生成代码(如循环展开、类型判断),实现“元编程”。例如:

4. 常量表达式接口:增强类型安全

通过 constexpr 定义接口(如 constexpr 函数、constexpr 类),强制调用者使用编译期常量,避免运行时错误。例如:

七、常见误区与注意事项

  1. “constexpr 变量一定在编译期存储”:错误。constexpr 仅要求“值在编译期确定”,存储位置仍由编译器决定(如可能存储在数据段,也可能直接内联到代码中)。
  2. “constexpr 函数只能返回常量”:错误。constexpr 函数的返回值是否为常量,取决于实参——实参是常量表达式时返回常量,实参是运行时变量时返回普通值。
  3. “constexpr 与 const 完全等价”:错误。const 仅表示“值不可修改”,不要求“值在编译期确定”(如 const int a = rand(); 是合法的,但 constexpr int a = rand(); 是错误的);而 constexpr 同时要求“不可修改”和“编译期确定”。
  4. “C++20 后 constexpr 可随意使用动态内存”:错误。C++20 允许 constexpr 函数使用 new,但必须在编译期通过 delete 释放(否则编译器报错),无法将动态内存泄露到运行时。

补充const与constexpr

constconstexpr 修饰函数时的作用和场景完全不同,核心区别在于:const 关注函数是否修改对象状态(仅用于成员函数),而 constexpr 关注函数是否能在编译时求值(可用于任意函数)。

1.const修饰函数(仅适用于类的非静态成员函数)

2.constexpr修饰函数(适用于任意函数:普通函数、成员函数、构造函数等)

3. 核心对比表

维度const 修饰函数constexpr 修饰函数
适用范围仅类的非静态成员函数任意函数(普通函数、成员函数、构造函数等)
核心作用保证函数不修改对象状态(只读约束)保证函数可在编译时求值(编译期计算)
本质运行时的对象状态保护编译时的计算能力支持
与常量表达式的关系无关(结果不能直接作为编译期常量)直接相关(结果可作为编译期常量)
能否同时使用可以(constexpr const 成员函数,双重约束)无冲突,constexpr 不影响 const 的作用

到此这篇关于C++中关键字constexpr的实现示例的文章就介绍到这了,更多相关C++ 关键字constexpr内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文