constexpr和const之间的区别

constexpr与const的区别
本文深入探讨C++中constexpr与const的区别,解释了constexpr如何保证变量为编译时常数,而const仅确保值在初始化后不可更改。文章详细介绍了constexpr在函数、对象和构造函数中的应用,以及何时应同时使用两者。

本文翻译自:Difference between `constexpr` and `const`

What's the difference between constexpr and const ? constexprconst什么区别?

  • When can I use only one of them? 什么时候只能使用其中之一?
  • When can I use both and how should I choose one? 什么时候可以同时使用两者?应该如何选择?

#1楼

参考:https://stackoom.com/question/xEDT/constexpr和const之间的区别


#2楼

const applies for variables , and prevents them from being modified in your code. const适用于变量 ,并防止它们在您的代码中被修改

constexpr tells the compiler that this expression results in a compile time constant value , so it can be used in places like array lengths, assigning to const variables, etc. The link given by Oli has a lot of excellent examples. constexpr告诉编译器该表达式产生一个编译时间常数值 ,因此可以在诸如数组长度,分配给const变量等地方使用。Oli提供的链接有很多很好的例子。

Basically they are 2 different concepts altogether, and can (and should) be used together. 基本上,它们总共是2个不同的概念,可以(并且应该)一起使用。


#3楼

Basic meaning and syntax 基本含义和语法

Both keywords can be used in the declaration of objects as well as functions. 这两个关键字都可以在对象和函数的声明中使用。 The basic difference when applied to objects is this: 应用于对象的基本区别是:

  • const declares an object as constant . const声明一个对象为常量 This implies a guarantee that, once initialized, the value of that object won't change, and the compiler can make use of this fact for optimizations. 这意味着保证,一旦初始化,该对象的值就不会改变,并且编译器可以利用这一事实进行优化。 It also helps prevent the programmer from writing code that modifies objects that were not meant to be modified after initialization. 它还有助于防止程序员编写代码来修改在初始化后不希望修改的对象。

  • constexpr declares an object as fit for use in what the Standard calls constant expressions . constexpr声明一个对象适合在标准调用的常量表达式中使用 But note that constexpr is not the only way to do this. 但是请注意, constexpr并非唯一的方法。

When applied to functions the basic difference is this: 当应用于函数时 ,基本区别是:

  • const can only be used for non-static member functions, not functions in general. const只能用于非静态成员函数,而不能用于一般函数。 It gives a guarantee that the member function does not modify any of the non-static data members. 它保证成员函数不会修改任何非静态数据成员。

  • constexpr can be used with both member and non-member functions, as well as constructors. constexpr可以与成员函数和非成员函数以及构造函数一起使用。 It declares the function fit for use in constant expressions . 它声明该函数适合在常量表达式中使用。 The compiler will only accept it if the function meets certain criteria (7.1.5/3,4), most importantly (†) : 仅当函数满足某些条件(7.1.5 / 3,4),最重要的是(†)时 ,编译器才会接受它:

    • The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single return statement is allowed. 函数体必须是非虚拟的,并且必须非常简单:除了typedef和static断言,仅允许单个return语句。 In the case of a constructor, only an initialization list, typedefs and static assert are allowed. 对于构造函数,仅允许使用初始化列表,typedef和静态断言。 ( = default and = delete are allowed, too, though.) (不过,也允许使用= default= delete 。)
    • As of C++14 the rules are more relaxed, what is allowed since then inside a constexpr function: asm declaration, a goto statement, a statement with a label other than case and default , try-block, definition of a variable of non-literal type, definition of a variable of static or thread storage duration, definition of a variable for which no initialization is performed. 从C ++ 14开始,规则更加宽松,此后允许在constexpr函数中使用: asm声明, goto语句,带有除casedefault之外的标签的语句,try-block,non变量的定义类型类型,静态或线程存储持续时间变量的定义,未执行初始化的变量的定义。
    • The arguments and the return type must be literal types (ie, generally speaking, very simple types, typically scalars or aggregates) 参数和返回类型必须是文字类型 (即,通常来说,非常简单的类型,通常是标量或集合)

Constant expressions 常数表达式

As said above, constexpr declares both objects as well as functions as fit for use in constant expressions. 如上所述, constexpr声明两个对象以及适合在常量表达式中使用的函数。 A constant expression is more than merely constant: 常量表达式不仅仅是常量:

  • It can be used in places that require compile-time evaluation, for example, template parameters and array-size specifiers: 它可以在需要编译时评估的地方使用,例如模板参数和数组大小说明符:

     template<int N> class fixed_size_list { /*...*/ }; fixed_size_list<X> mylist; // X must be an integer constant expression int numbers[X]; // X must be an integer constant expression 
  • But note: 但请注意:

    • Declaring something as constexpr does not necessarily guarantee that it will be evaluated at compile time. 将某些东西声明为constexpr并不一定保证它将在编译时进行评估。 It can be used for such, but it can be used in other places that are evaluated at run-time, as well. 可以用于此目的 ,但也可以在运行时评估的其他地方使用。

    • An object may be fit for use in constant expressions without being declared constexpr . 一个对象可能适合在常量表达式中使用, 而无需声明constexpr Example: 例:

       int main() { const int N = 3; int numbers[N] = {1, 2, 3}; // N is constant expression } 

    This is possible because N , being constant and initialized at declaration time with a literal, satisfies the criteria for a constant expression, even if it isn't declared constexpr . 这是可能的,因为N是常量,并且在声明时使用文字立即初始化,即使未声明constexpr ,它也满足常量表达式的条件。

So when do I actually have to use constexpr ? 那么,我什么时候真正必须使用constexpr

  • An object like N above can be used as constant expression without being declared constexpr . 像上面的N这样的对象可以用作常量表达式, 而无需声明constexpr This is true for all objects that are: 对于以下所有对象都是如此:

    • const
    • of integral or enumeration type and 整型或枚举类型的,并且
    • initialized at declaration time with an expression that is itself a constant expression 在声明时使用本身就是常量表达式的表达式进行初始化

    [This is due to §5.19/2: A constant expression must not include a subexpressions that involves "an lvalue-to-rvalue modification unless […] a glvalue of integral or enumeration type […]" Thanks to Richard Smith for correcting my earlier claim that this was true for all literal types.] [这是由于第5.19 / 2条所致:常量表达式不得包含涉及“从左值到右值的修改,除非[...]整数或枚举类型的[...]的glvalue”的子表达式”感谢Richard Smith纠正了早些时候声称对所有文字类型都是如此。]

  • For a function to be fit for use in constant expressions, it must be explicitly declared constexpr ; 为了使函数适合在常量表达式中使用, 必须将其显式声明为constexpr it is not sufficient for it merely to satisfy the criteria for constant-expression functions. 仅满足常数表达式函数的标准是不够的。 Example: 例:

     template<int N> class list { }; constexpr int sqr1(int arg) { return arg * arg; } int sqr2(int arg) { return arg * arg; } int main() { const int X = 2; list<sqr1(X)> mylist1; // OK: sqr1 is constexpr list<sqr2(X)> mylist2; // wrong: sqr2 is not constexpr } 

When can I / should I use both, const and constexpr together? 我什么时候可以同时使用constconstexpr

A. In object declarations. A.在对象声明中。 This is never necessary when both keywords refer to the same object to be declared. 当两个关键字都引用相同的要声明的对象时,就永远不需要这样做。 constexpr implies const . constexpr暗示const

constexpr const int N = 5;

is the same as 是相同的

constexpr int N = 5;

However, note that there may be situations when the keywords each refer to different parts of the declaration: 但是,请注意,在某些情况下,关键字每个都引用声明的不同部分:

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &N;
}

Here, NP is declared as an address constant-expression, ie an pointer that is itself a constant expression. 此处, NP被声明为地址常量表达式,即本身就是常量表达式的指针。 (This is possible when the address is generated by applying the address operator to a static/global constant expression.) Here, both constexpr and const are required: constexpr always refers to the expression being declared (here NP ), while const refers to int (it declares a pointer-to-const). (当通过将地址运算符应用于静态/全局常量表达式来生成地址时,这是可能的。)在这里, constexprconst都是必需的: constexpr始终引用要声明的表达式(此处为NP ),而const引用int (它声明了一个指向常量的指针)。 Removing the const would render the expression illegal (because (a) a pointer to a non-const object cannot be a constant expression, and (b) &N is in-fact a pointer-to-constant). 删除const将使表达式非法(因为(a)指向非const对象的指针不能是常量表达式,并且(b) &N实际上是指向常量的指针)。

B. In member function declarations. B.在成员函数声明中。 In C++11, constexpr implies const , while in C++14 and C++17 that is not the case. 在C ++ 11中, constexpr隐含const ,而在C ++ 14和C ++ 17中则并非如此。 A member function declared under C++11 as 在C ++ 11下声明为的成员函数为

constexpr void f();

needs to be declared as 需要声明为

constexpr void f() const;

under C++14 in order to still be usable as a const function. 为了仍然可用作const函数,请在C ++ 14下使用。


#4楼

According to book of "The C++ Programming Language 4th Editon" by Bjarne Stroustrup 根据Bjarne Stroustrup撰写的“ The C ++ Programming Language 4th Editon”一书
const : meaning roughly ''I promise not to change this value'' (§7.5). const :大致意思是“我保证不会更改此值”(第7.5节)。 This is used primarily to specify interfaces, so that data can be passed to functions without fear of it being modified. 这主要用于指定接口,以便可以将数据传递给函数而不必担心会被修改。
The compiler enforces the promise made by const. 编译器执行const作出的承诺。
constexpr : meaning roughly ''to be evaluated at compile time'' (§10.4). constexpr :大致意思是“在编译时求值”(第10.4节)。 This is used primarily to specify constants, to allow 这主要用于指定常量,以允许
For example: 例如:

const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4∗square(var); // error : var is not a constant expression
const double max3 = 1.4∗square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression

For a function to be usable in a constant expression, that is, in an expression that will be evaluated by the compiler, it must be defined constexpr . 为了使函数在常量表达式(即将由编译器求值的表达式)中使用,必须将其定义为constexpr
For example: 例如:

constexpr double square(double x) { return x∗x; }


To be constexpr, a function must be rather simple: just a return-statement computing a value. 要成为constexpr,函数必须非常简单:仅是一个计算值的返回语句。 A constexpr function can be used for non-constant arguments, but when that is done the result is not a constant expression. constexpr函数可用于非恒定参数,但这样做的结果不是恒定表达式。 We allow a constexpr function to be called with non-constant-expression arguments in contexts that do not require constant expressions, so that we don't hav e to define essentially the same function twice: once for constant expressions and once for variables. 我们允许在不需要常量表达式的上下文中使用非常量表达式参数调用constexpr函数,因此我们不必两次定义本质上相同的函数:一次用于常量表达式,一次用于变量。
In a few places, constant expressions are required by language rules (eg, array bounds (§2.2.5, §7.3), case labels (§2.2.4, §9.4.2), some template arguments (§25.2), and constants declared using constexpr). 在某些地方,语言规则(例如,数组边界(第2.2.5节,第7.3节),大小写标签(第2.2.4节,第9.4.2节),一些模板参数(第25.2节)和使用constexpr声明的常量)。 In other cases, compile-time evaluation is important for performance. 在其他情况下,编译时评估对于性能很重要。 Independently of performance issues, the notion of immutability (of an object with an unchangeable state) is an important design concern (§10.4). 与性能问题无关,(具有不变状态的对象的)不变性的概念是一个重要的设计问题(第10.4节)。


#5楼

Overview 总览

  • const guarantees that a program does not change an object's value . const保证程序不会更改对象的value However, const does not guarantee which type of initialization the object undergoes. 但是, const不保证对象将进行哪种类型的初始化。

    Consider: 考虑:

     const int mx = numeric_limits<int>::max(); // OK: runtime initialization 

    The function max() merely returns a literal value. 函数max()仅返回文字值。 However, because the initializer is a function call, mx undergoes runtime initialization. 但是,由于初始化程序是函数调用,因此mx会进行运行时初始化。 Therefore, you cannot use it as a constant expression : 因此,不能将其用作常量表达式

     int arr[mx]; // error: “constant expression required” 
  • constexpr is a new C++11 keyword that rids you of the need to create macros and hardcoded literals. constexpr是一个新的C ++ 11关键字,使您无需创建宏和硬编码的文字。 It also guarantees, under certain conditions, that objects undergo static initialization . 它还可以确保在某些条件下对象可以进行静态初始化 It controls the evaluation time of an expression. 它控制表达式的评估时间。 By enforcing compile-time evaluation of its expression , constexpr lets you define true constant expressions that are crucial for time-critical applications, system programming, templates, and generally speaking, in any code that relies on compile-time constants. 通过对表达式的编译时求值constexpr可以让您定义真正的常量表达式 ,这些表达式对于依赖于时间的应用程序,系统编程,模板以及通常来说对于依赖编译时常量的任何代码都至关重要。

Constant-expression functions 常数表达函数

A constant-expression function is a function declared constexpr . 常量表达式函数是声明为constexpr的函数。 Its body must be non-virtual and consist of a single return statement only, apart from typedefs and static asserts. 它的主体必须是非虚拟的,并且除了typedef和static断言外,只能由单个return语句组成。 Its arguments and return value must have literal types. 它的参数和返回值必须具有文字类型。 It can be used with non-constant-expression arguments, but when that is done the result is not a constant expression. 可以将其与非常量表达式参数一起使用,但是完成后的结果将不是常量表达式。

A constant-expression function is meant to replace macros and hardcoded literals without sacrificing performance or type safety. 常量表达式函数旨在替换硬编码文字,而不会牺牲性能或类型安全性。

constexpr int max() { return INT_MAX; }           // OK
constexpr long long_max() { return 2147483647; }  // OK
constexpr bool get_val()
{
    bool res = false;
    return res;
}  // error: body is not just a return statement

constexpr int square(int x)
{ return x * x; }  // OK: compile-time evaluation only if x is a constant expression
const int res = square(5);  // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y);          // OK: runtime evaluation of square(y)

Constant-expression objects 常量表达式对象

A constant-expression object is an object declared constexpr . 常量表达式对象是声明为constexpr的对象。 It must be initialized with a constant expression or an rvalue constructed by a constant-expression constructor with constant-expression arguments. 它必须使用常量表达式或由带有常量表达式参数的常量表达式构造函数构造的右值初始化。

A constant-expression object behaves as if it was declared const , except that it requires initialization before use and its initializer must be a constant expression. 常量表达式对象的行为就像声明为const ,只是它在使用前需要进行初始化并且其初始化程序必须是常量表达式。 Consequently, a constant-expression object can always be used as part of another constant expression. 因此,常量表达式对象始终可以用作另一个常量表达式的一部分。

struct S
{
    constexpr int two();      // constant-expression function
private:
    static constexpr int sz;  // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
    Small = S::two(),  // error: S::two() called before it was defined
    Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()];  // OK: s.two() called after its definition

Constant-expression constructors 常量表达式构造函数

A constant-expression constructor is a constructor declared constexpr . 常量表达式构造函数是声明为constexpr的构造函数。 It can have a member initialization list but its body must be empty, apart from typedefs and static asserts. 它可以有一个成员初始化列表,但是除了typedef和static断言之外,它的主体必须为空。 Its arguments must have literal types. 它的参数必须具有文字类型。

A constant-expression constructor allows the compiler to initialize the object at compile-time, provided that the constructor's arguments are all constant expressions. 常量表达式构造函数允许编译器在编译时初始化对象,前提是构造函数的参数均为常量表达式。

struct complex
{
    // constant-expression constructor
    constexpr complex(double r, double i) : re(r), im(i) { }  // OK: empty body
    // constant-expression functions
    constexpr double real() { return re; }
    constexpr double imag() { return im; }
private:
    double re;
    double im;
};
constexpr complex COMP(0.0, 1.0);         // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0);              // error: x is not a constant expression
const complex cx2(x, 1);                  // OK: runtime initialization
constexpr double xx = COMP.real();        // OK: compile-time initialization
constexpr double imaglval = COMP.imag();  // OK: compile-time initialization
complex cx3(2, 4.6);                      // OK: runtime initialization

Tips from the book Effective Modern C++ by Scott Meyers about constexpr : 斯科特·迈耶斯(Scott Meyers)写的《 有效的现代C ++》中有关constexpr

  • constexpr objects are const and are initialized with values known during compilation; constexpr对象是const,并使用编译期间已知的值进行初始化;
  • constexpr functions produce compile-time results when called with arguments whose values are known during compilation; constexpr函数在编译期间用其值已知的参数调用时会产生编译时结果。
  • constexpr objects and functions may be used in a wider range of contexts than non- constexpr objects and functions; 与非constexpr对象和函数相比, constexpr对象和函数可以在更广泛的上下文中使用。
  • constexpr is part of an object's or function's interface. constexpr是对象或函数接口的一部分。

Source: Using constexpr to Improve Security, Performance and Encapsulation in C++ . 资料来源: 使用constexpr改善C ++的安全性,性能和封装


#6楼

As @0x499602d2 already pointed out, const only ensures that a value cannot be changed after initialization where as constexpr (introduced in C++11) guarantees the variable is a compile time constant. 正如@ 0x499602d2已经指出的那样, const仅确保初始化后不能更改值,而constexpr (在C ++ 11中引入)保证变量是编译时间常数。
Consider the following example(from LearnCpp.com): 考虑以下示例(来自LearnCpp.com):

cout << "Enter your age: ";
int age;
cin >> age;

const int myAge{age};        // works
constexpr int someAge{age};  // error: age can only be resolved at runtime
### C++ 中 `constexpr` 与 `const` 的关键区别、使用场景及语义差异 #### 1. **定义与基本概念** - `const` 是用于表示变量或对象在其生命周期内不可修改的一种关键字。它可以应用于编译时常量运行时常量,但其值不一定能在编译期确定[^5]。 - `constexpr` 则是一种更严格的常量声明方式,旨在确保表达式的值在编译期即可计算得出,并且能够参与模板元编程或其他需要纯编译期求值的上下文中[^1]。 --- #### 2. **功能对比** | 特性 | `const` | `constexpr` | |--------------------------|-----------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| | 编译期计算 | 不一定能够在编译期计算出结果;可能仅限于运行时初始化 | 强制要求初始化表达式必须在编译期完成计算 | | 类型安全性 | 可能缺乏类型检查 | 提供更强的类型安全保障 | | 支持复杂度 | 主要适用于简单数据类型的常量 | 支持复杂的表达式、函数甚至部分容器的操作 | | 初始化时机 | 允许延迟到运行时初始化 | 必须立即在编译期完成初始化 | 例如: ```cpp // const 示例 const int a = 42; // 编译器可以选择是否在编译期评估 const int b = some_function(); // 需要在运行时调用函数获取值 // constexpr 示例 constexpr int c = 42; // 必须在编译期计算 constexpr int d = some_constexpr_function(); // 函数需为constexpr类型,且返回值也在编译期确定 ``` --- #### 3. **使用场景分析** ##### (1)`const` 的典型使用场景 - 当只需要表明某个变量在整个程序执行过程中不会发生变化时,可以使用 `const`。 - 对于那些无法提前知道确切数值而只能通过运行时操作得到的结果,仍然适用 `const` 来保护这些资源不被非法改动。 示例代码如下: ```cpp void example(const std::string& str); // 参数传递中保持字符串不变 class Example { private: const double pi = 3.14159; // 成员变量pi一旦设定就不应再改变 }; ``` ##### (2)`constexpr` 的优势领域 由于 `constexpr` 更加注重性能优化以及静态解析能力,因此特别适合以下情况: - 创建真正意义上完全由编译器处理完毕后再嵌入目标文件当中的常数; - 构建高度抽象化的通用算法库(如STL实现细节里的诸多地方都广泛采用了此类技术); - 实现类似于宏定义却又兼具良好调试特性的替代方案[^2]。 举个例子来说就是数组大小这样的参数往往希望尽早确认下来以便后续安排内存布局等工作流程顺利开展下去: ```cpp constexpr size_t arraySize() {return 10;} int myArray[arraySize()]; // 数组长度是在编译阶段就已知的信息 ``` 此外还有关于Lambda表达式的增强应用也是近年来新增的重要特性之一[^3]: ```cpp auto lambdaExample = [](int x) constexpr -> int{ return ++x; }; static_assert(lambdaExample(5)==6,""); // 测试lambda能否正常工作于编译环境下 ``` --- #### 4. **语义层面的区别** 从更高层次的角度来看待两者之间的关系,则会发现它们各自承担着不同的职责范围: - **`const`** 更像是面向使用者的一个契约约定,告诉程序员这个东西不应该再去动它了,至于背后到底有没有真的做到这一点则要看具体情况而定。 - **`constexpr`** 却完全不同,它是站在编译系统的立场出发制定规则,明确规定只有满足特定条件下的事物才能够被打上这样一个标签,从而使得整个软件开发过程变得更加高效可靠[^1]。 --- ### 结论 综上所述,在选择使用 `const` 还是 `constexpr` 时应该充分考虑当前需求背景因素的影响,合理权衡二者之间微妙平衡点所在位置。一般来说如果仅仅是想单纯表达某项属性不可更改的意思的话那么选用前者就够了;但如果追求极致效率或者涉及到更多深层次定制化扩展可能性的时候就应该优先考虑后者所带来的额外好处。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值