当前位置:首页 > 4444临时 > 正文内容

c++中const和constexpr的区别

4444临时3周前 (08-27)

核心概念总结

  • const:主要是一个语义/承诺概念,意思是“只读”。它告诉编译器和程序员:“这个对象初始化后,我不应该通过这个标识符去修改它”。它不关心这个值是在编译期还是运行期确定的。

  • constexpr:主要是一个时间点概念,意思是“常量表达式”。它告诉编译器:“这个值(或函数)必须在编译期就能计算出来”。因为它必须在编译期确定,所以它天然就是const的(即只读的)。

可以把它们的关系理解为:所有的constexpr都是const,但并非所有的const都是constexpr


详细区别与示例

我们从变量和函数两个角度来分析。

1. 用于变量(Objects)

特性constconstexpr
核心含义只读。运行时或编译期初始化均可。编译期常量。必须在编译期初始化。
初始化时间运行时编译期必须是编译期
要求的严格性较宽松。可以用运行时计算的结果初始化。非常严格。初始化表达式必须是编译期可计算的。
用途保证程序逻辑上不修改变量。用于需要编译期常量的场景(如数组大小、模板参数等)。

示例代码:

#include <iostream>

int get_value() {
    return 10;
}

constexpr int get_value_constexpr() {
    return 10;
}

int main() {
    int runtime_var = 10; // 显然是运行时变量

    // 1. const 变量
    const int const_var_1 = 10;           // 编译期初始化,是 const,也是编译期常量
    const int const_var_2 = get_value();  // **运行时**初始化,是 const,但不是编译期常量

    // 2. constexpr 变量
    constexpr int constexpr_var_1 = 10;                         // 正确:字面量,编译期可知
    constexpr int constexpr_var_2 = get_value_constexpr();      // 正确:constexpr函数在编译期计算
    // constexpr int constexpr_var_3 = get_value();            // **错误!** get_value() 不是 constexpr 函数,无法在编译期求值
    // constexpr int constexpr_var_4 = const_var_2;            // **错误!** const_var_2 本身是运行时初始化的,不是编译期常量

    // 关键区别体现在需要编译期常量的场景
    int array_1[const_var_1];     // OK (在大多数编译器下): 因为 const_var_1 是编译期常量
    // int array_2[const_var_2];  // **错误!** const_var_2 不是编译期常量,不能用于定义数组大小
    int array_3[constexpr_var_1]; // OK: constexpr_var_1 肯定是编译期常量

    // 对于模板元编程(如 std::array)
    std::array<int, const_var_1> arr1;     // 通常OK
    // std::array<int, const_var_2> arr2;  // 错误!
    std::array<int, constexpr_var_1> arr3; // 总是OK

    return 0;
}

2. 用于函数(Functions)

特性const成员函数constexpr函数
核心含义承诺不修改类的成员变量mutable成员除外)。表示该函数有可能在编译期被调用和执行
调用时机只能在运行时调用。既可以在编译期调用,也可以在运行时调用。
函数体要求函数体内不能修改非mutable成员。函数体在C++11/14/17/20标准下有严格限制,但标准在逐步放宽。通常要求函数体非常简单(C++11),后来可以包含循环、局部变量甚至if等(C++14+)。

核心思想:constexpr函数就像一个“承诺”,承诺如果我给你的参数是编译期常量,那么我就能在编译期计算出结果。如果给的是运行时变量,那么它就在运行时计算,像一个普通函数一样。

示例代码:

#include <array>

// 一个 constexpr 函数
constexpr int square(int n) {
    return n * n;
}

class MyClass {
public:
    // const 成员函数:承诺不修改成员状态
    int get_value() const {
        return member_var;
    }

    // constexpr 构造函数:允许在编译期创建该类的常量表达式
    constexpr MyClass(int x) : member_var(x) {}
    
    // constexpr 成员函数
    constexpr int get_value_constexpr() const {
        return member_var;
    }

private:
    int member_var;
};

int main() {
    int x = 5;

    // 使用 constexpr 函数
    constexpr int compiled_time_result = square(10); // 编译期计算
    int run_time_result = square(x);                 // 运行时计算

    // 使用 constexpr 构造函数
    constexpr MyClass obj(15); // 必须在编译期初始化
    constexpr int val = obj.get_value_constexpr(); // 编译期调用 constexpr 成员函数

    // 用于需要编译期常量的场景
    std::array<int, square(5)> arr1; // 数组大小在编译期计算为 25
    // std::array<int, square(x)> arr2; // 错误!x 不是编译期常量

    return 0;
}

总结与使用建议

场景推荐使用原因
需要一个绝对不会改变的值constconstexpr语义上是“只读”的。
需要一个值,并且它必须在编译期就知道(如数组大小、模板参数、case标签)必须使用constexprconst不一定能在编译期知道值。
设计一个函数,希望它既能用于编译期计算也能用于运行时计算constexpr提高了函数的可用性和性能(编译期计算可优化为字面量)。
一个类的成员函数,承诺不修改对象状态const这是const成员函数的专属用途。
一个类的构造函数,希望该类能用于编译期常量constexpr允许在编译期创建该类的常量表达式对象。

现代C++开发的最佳实践是:

优先使用constexpr。如果你需要一个常量,并且它的值可以在编译期确定,那么毫不犹豫地使用constexpr。这为你的代码提供了最大的优化可能性和灵活性。只有在确实需要在运行时初始化一个只读变量时,才使用普通的const


转载请注明出处。

本文链接:http://www.pythonopen.com/?id=7923

相关文章

[12][20][单选]下列关于文件系统中文件的描述中,哪一个是正确的

构成文件内容的基本单位为信息项文件的内容没有顺序关系文件内容都是由操作系统程序并使用的用户需要关注文...

[12][21][单选]下列哪一项是执行打开文件操作时由操作系统返回的

文件名文件号文件描述符文件物理位置答案文件描述符解析执行打开文件操作时由操作系统返回的信息是文件描述...

[12][22][单选]下列关于实现创建文件操作的描述中,哪一个是错误的

创建文件操作完成后,该文件得到一个新的文件控制块FCB创建文件操作完成后,操作系统给该文件分配一定的...