C++ `constexpr` 和 `const` 之间的区别

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14116003/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:00:21  来源:igfitidea点击:

Difference between `constexpr` and `const`

c++c++11constconstexpr

提问by MBZ

What's the difference between constexprand const?

constexpr和 和有const什么区别?

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

采纳答案by jogojapan

Basic meaning and syntax

基本含义和语法

Both keywords can be used in the declaration of objects as well as functions. The basic difference when applied to objectsis this:

这两个关键字都可以在对象和函数的声明中使用。应用于对象时的基本区别是:

  • constdeclares an object as constant. 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.

  • constexprdeclares an object as fit for use in what the Standard calls constant expressions. But note that constexpris not the only way to do this.

  • const将对象声明为常量。这意味着保证一旦初始化,该对象的值就不会改变,并且编译器可以利用这一事实进行优化。它还有助于防止程序员编写修改初始化后不打算修改的对象的代码。

  • constexpr声明一个对象适合用于标准所称的常量表达式。但请注意,这constexpr不是执行此操作的唯一方法。

When applied to functionsthe basic difference is this:

当应用于函数时,基本的区别是:

  • constcan only be used for non-static member functions, not functions in general. It gives a guarantee that the member function does not modify any of the non-static data members.

  • constexprcan be used with both member and non-member functions, as well as constructors. 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 (†):

    • The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single returnstatement is allowed. In the case of a constructor, only an initialization list, typedefs and static assert are allowed. (= defaultand = deleteare allowed, too, though.)
    • As of C++14 the rules are more relaxed, what is allowed since then inside a constexpr function: asmdeclaration, a gotostatement, a statement with a label other than caseand 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.
    • The arguments and the return type must be literal types(i.e., generally speaking, very simple types, typically scalars or aggregates)
  • const只能用于非静态成员函数,不能用于一般函数。它保证成员函数不会修改任何非静态数据成员。

  • constexpr可以与成员和非成员函数以及构造函数一起使用。它声明了适合在常量表达式中使用的函数。编译器仅在函数满足特定条件 (7.1.5/3,4) 时才会接受它,最重要的是(†)

    • 函数体必须是非虚拟的并且非常简单:除了 typedef 和静态断言之外,只return允许使用单个语句。在构造函数的情况下,只允许初始化列表、typedef 和静态断言。(= default= delete也被允许使用,但。)
    • 从 C++14 开始,规则更加宽松,从那时起在 constexpr 函数中允许的内容:asm声明,goto语句,带有除caseand之外的标签的语句default,try-block,非文字类型变量的定义,静态或线程存储持续时间的变量的定义,未执行初始化的变量的定义。
    • 参数和返回类型必须是文字类型(即,一般来说,非常简单的类型,通常是标量或聚合)

Constant expressions

常量表达式

As said above, constexprdeclares both objects as well as functions as fit for use in constant expressions. A constant expression is more than merely constant:

如上所述,constexpr声明适合在常量表达式中使用的对象和函数。常量表达式不仅仅是常量:

  • 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 constexprdoes not necessarily guarantee that it will be evaluated at compile time. It can be usedfor such, but it can be used in other places that are evaluated at run-time, as well.

    • An object maybe fit for use in constant expressions withoutbeing declared 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.

  • 它可以用在需要编译时评估的地方,例如模板参数和数组大小说明符:

    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
    
  • 但请注意:

    • 声明一些东西constexpr并不一定保证它会在编译时被评估。它可以用于此类,但它也可以用于在运行时进行评估的其他地方。

    • 一个对象可能适合在常量表达式中使用而无需声明constexpr。例子:

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

    这是可能的,因为N是常量并在声明时用文字初始化,满足常量表达式的标准,即使它没有被声明constexpr

So when do I actually have to use constexpr?

那么我constexpr什么时候真正必须使用?

  • An objectlike Nabove can be used as constant expression withoutbeing declared 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.]

  • For a functionto be fit for use in constant expressions, it mustbe explicitly declared 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
    }
    
  • 像上面这样的对象N可以用作常量表达式而无需声明constexpr。这适用于所有对象:

    • const
    • 整数或枚举类型
    • 在声明时用本身是常量表达式的表达式初始化

    [这是由于 §5.19/2:常量表达式不得包含涉及“左值到右值修改的子表达式,除非 [...] 整数或枚举类型的泛左值 [...]” 感谢 Richard Smith 更正我的早先声称这适用于所有文字类型。]

  • 对于适合在常量表达式中使用的函数必须显式声明它constexpr;仅仅满足常量表达式函数的标准是不够的。例子:

    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, constand constexprtogether?

我什么时候可以/我应该同时使用,constconstexpr在一起呢?

A. In object declarations.This is never necessary when both keywords refer to the same object to be declared. constexprimplies const.

A. 在对象声明中。当两个关键字都指向要声明的同一个对象时,这是不必要的。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, NPis declared as an address constant-expression, i.e. an pointer that is itself a constant expression. (This is possible when the address is generated by applying the address operator to a static/global constant expression.) Here, both constexprand constare required: constexpralways refers to the expression being declared (here NP), while constrefers to int(it declares a pointer-to-const). Removing the constwould render the expression illegal (because (a) a pointer to a non-const object cannot be a constant expression, and (b) &Nis in-fact a pointer-to-constant).

在这里,NP被声明为地址常量表达式,即一个指针,它本身就是一个常量表达式。(当通过将地址运算符应用于静态/全局常量表达式来生成地址时,这是可能的。)在这里,constexprconst都是必需的:constexpr始终指代声明的表达式(此处NP),而const指代int(它声明了一个指针-到常量)。删除const会使表达式非法(因为 (a) 指向非常量对象的指针不能是常量表达式,而 (b)&N实际上是指向常量的指针)。

B. In member function declarations.In C++11, constexprimplies const, while in C++14 and C++17 that is not the case. A member function declared under C++11 as

B. 在成员函数声明中。在 C++11 中,constexpr蕴涵const,而在 C++14 和 C++17 中,情况并非如此。在 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 constfunction.

在 C++14 下,以便仍可用作const函数。

回答by Karthik T

constapplies for variables, and prevents them from being modifiedin your code.

const适用于variables,并防止它们在您的代码中被修改

constexprtells the compiler that this expressionresults in a compile time constant value, so it can be used in places like array lengths, assigning to constvariables, etc. The linkgiven by Oli has a lot of excellent examples.

constexpr告诉编译器这个表达式的结果是一个编译时常量 value,所以它可以用在数组长度、赋值给const变量等地方。Oli 给出的链接有很多很好的例子。

Basically they are 2 different concepts altogether, and can (and should) be used together.

基本上它们是两个不同的概念,可以(并且应该)一起使用。

回答by zangw

Overview

概述

  • constguarantees that a program does not change an object's value. However, constdoes not guarantee which type of initialization the object undergoes.

    Consider:

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

    The function max()merely returns a literal value. However, because the initializer is a function call, mxundergoes runtime initialization. Therefore, you cannot use it as a constant expression:

    int arr[mx];  // error: “constant expression required”
    
  • constexpris a new C++11 keyword that rids you of the need to create macros and hardcoded literals. 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, constexprlets you define true constant expressionsthat are crucial for time-critical applications, system programming, templates, and generally speaking, in any code that relies on compile-time constants.

  • const保证程序不会改变对象的值。但是,const不能保证对象经历哪种类型的初始化。

    考虑:

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

    该函数max()仅返回一个文字值。但是,由于初始化程序是函数调用,因此mx会进行运行时初始化。因此,您不能将其用作常量表达式

    int arr[mx];  // error: “constant expression required”
    
  • constexpr是一个新的 C++11 关键字,它使您无需创建宏和硬编码文字。它还保证,在某些条件下,对象进行静态初始化。它控制表达式的计算时间。通过强制对其表达式进行编译时求值constexpr您可以定义真正的常量表达式,这些表达式对于时间关键型应用程序、系统编程、模板以及一般而言,在依赖编译时常量的任何代码中都是至关重要的。

Constant-expression functions

常量表达式函数

A constant-expression functionis a function declared constexpr. Its body must be non-virtual and consist of a single return statement only, apart from typedefs and static asserts. 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.

常数表达式函数是声明的函数constexpr。除了 typedef 和静态断言之外,它的主体必须是非虚拟的,并且仅由单个 return 语句组成。它的参数和返回值必须具有文字类型。它可以与非常量表达式参数一起使用,但是当这样做时,结果不是一个常量表达式。

A constant-expression function is meant to replace macrosand hardcoded literalswithout 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 objectis an object declared constexpr. It must be initialized with a constant expression or an rvalue constructed by a constant-expression constructor with constant-expression arguments.

常数表达式对象是声明的目的constexpr。它必须使用常量表达式或由具有常量表达式参数的常量表达式构造函数构造的右值进行初始化。

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. Consequently, a constant-expression object can always be used as part of another constant expression.

一个常量表达式对象的行为就像它被声明一样const,除了它在使用前需要初始化并且它的初始化器必须是一个常量表达式。因此,常量表达式对象始终可以用作另一个常量表达式的一部分。

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 constructoris a constructor declared constexpr. It can have a member initialization list but its body must be empty, apart from typedefs and static asserts. Its arguments must have literal types.

常数表达式构造是声明的构造constexpr。它可以有一个成员初始化列表,但它的主体必须是空的,除了 typedef 和静态断言。它的参数必须具有文字类型。

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所著《Effective Modern C++》一书中的提示constexpr

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

Source: Using constexpr to Improve Security, Performance and Encapsulation in C++.

来源: 使用 constexpr 提高 C++ 中的安全性、性能和封装

回答by Mustafa Ekici

According to book of "The C++ Programming Language 4th Editon" by Bjarne Stroustrup
? const: meaning roughly ‘‘I promise not to change this value'' (§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.
? constexpr: meaning roughly ‘‘to be evaluated at compile time'' (§10.4). This is used primarily to specify constants, to allow
For example:

根据 Bjarne Stroustrup 的“C++ 编程语言第 4 版”一书
const:大致意思是“我保证不会改变这个值”(第 7.5 节)。这主要用于指定接口,以便数据可以传递给函数而不必担心被修改。
编译器强制执行 const 做出的承诺。
? constexpr:大致意思是“在编译时进行评估”(第 10.4 节)。这主要用于指定常量,以允许
例如:

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.
For example:

要使函数可在常量表达式中使用,即在将由编译器计算的表达式中,它必须定义为constexpr
例如:

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


To be constexpr, a function must be rather simple: just a return-statement computing a value. A constexpr function can be used for non-constant arguments, but when that is done the result is not a constant expression. 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.
In a few places, constant expressions are required by language rules (e.g., 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). 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).


要成为 constexpr,函数必须相当简单:只是一个计算值的返回语句。constexpr 函数可用于非常量参数,但完成后结果不是常量表达式。我们允许在不需要常量表达式的上下文中使用非常量表达式参数调用 constexpr 函数,因此我们不必两次定义本质上相同的函数:一次用于常量表达式,一次用于变量。
在一些地方,语言规则需要常量表达式(例如,数组边界(第 2.2.5 节、第 7.3 节)、案例标签(第 2.2.4 节、第 9.4.2 节)、一些模板参数(第 25.2 节)和使用 constexpr 声明的常量)。在其他情况下,编译时评估对性能很重要。独立于性能问题,不变性(具有不可更改状态的对象)的概念是一个重要的设计问题(第 10.4 节)。

回答by Timmy_A

Both constand constexprcan be applied to variables and functions. Even though they are similar to each other, in fact they are very different concepts.

二者constconstexpr可以应用到变量和函数。尽管它们彼此相似,但实际上它们是非常不同的概念。

Both constand constexprmean that their values can't be changed after their initialization. So for example:

双方constconstexpr意味着他们的价值不能被他们的初始化后改变。例如:

const int x1=10;
constexpr int x2=10;

x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.

The principal difference between constand constexpris the time when their initialization values are known (evaluated). While the values of constvariables can be evaluated at both compile time and runtime, constexprare always evaluated at compile time. For example:

const和之间的主要区别在于constexpr它们的初始化值已知(评估)的时间。虽然const变量的值可以在编译时和运行时计算,constexpr但总是在编译时计算。例如:

int temp=rand(); // temp is generated by the the random generator at runtime.

const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.

The key advantage to know if the value is known at compile time or runtime is the fact that compile time constants can be used whenever compile time constants are needed. For instance, C++ doesn't allow you to specify C-arrays with the variable lengths.

知道该值在编译时还是运行时已知的关键优势在于,只要需要编译时常量,就可以使用编译时常量。例如,C++ 不允许您指定具有可变长度的 C 数组。

int temp=rand(); // temp is generated by the the random generator at runtime.

int array1[10]; // OK.
int array2[temp]; // ERROR.

So it means that:

所以这意味着:

const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.


int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.

So constvariables can define both compile time constantslike size1that can be used to specify array sizes and runtime constantslike size2that are known only at runtime and can't be used to define array sizes. On the other hand constexpralways define compile time constants that can specify array sizes.

这样const的变量可以定义两个编译时间常数size1,可被用来指定数组的大小和运行时间常数size2,只有在运行时是已知的,并且不能被用来定义阵列的尺寸。另一方面,constexpr始终定义可以指定数组大小的编译时常量。

Both constand constexprcan be applied to functions too. A constfunction must be a member function (method, operator) where application of constkeyword means that the method can't change the values of their member (non-static) fields. For example.

双方constconstexpr可以应用到的功能了。甲const函数必须是一个成员函数(方法,操作者)的,其中应用const关键字装置,该方法不能改变他们的构件(非静态)字段的值。例如。

class test
{
   int x;

   void function1()
   {
      x=100; // OK.
   }

   void function2() const
   {
      x=100; // ERROR. The const methods can't change the values of object fields.
   }
};

A constexpris a different concept. It marks a function (member or non-member) as the function that can be evaluated at compile time if compile time constants are passed as their arguments. For example you can write this.

Aconstexpr是一个不同的概念。如果编译时常量作为参数传递,它将函数(成员或非成员)标记为可以在编译时评估的函数。例如你可以写这个。

constexpr int func_constexpr(int X, int Y)
{
    return(X*Y);
}

int func(int X, int Y)
{
    return(X*Y);
}

int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.

int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time.

By the way the constexprfunctions are the regular C++ functions that can be called even if non-constant arguments are passed. But in that case you are getting the non-constexpr values.

顺便说一下,这些constexpr函数是即使传递了非常量参数也可以调用的常规 C++ 函数。但在这种情况下,您将获得非 constexpr 值。

int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.

The constexprcan be also applied to the member functions (methods), operators and even constructors. For instance.

constexpr也可应用于所述成员函数(方法),操作者和甚至构造函数。例如。

class test2
{
    static constexpr int function(int value)
    {
        return(value+1);
    }

    void f()
    {
        int x[function(10)];


    }
};

A more 'crazy' sample.

一个更“疯狂”的样本。

class test3
{
    public:

    int value;

    // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
    constexpr int getvalue() const
    {
        return(value);
    }

    constexpr test3(int Value)
        : value(Value)
    {
    }
};


constexpr test3 x(100); // OK. Constructor is constexpr.

int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.

回答by Lokesh Meher

As @0x499602d2 already pointed out, constonly ensures that a value cannot be changed after initialization where as constexpr(introduced in C++11) guarantees the variable is a compile time constant.
Consider the following example(from LearnCpp.com):

正如@0x499602d2 已经指出的那样,const仅确保初始化后不能更改值,而 as constexpr(在 C++11 中引入)保证变量是编译时常量。
考虑以下示例(来自 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

回答by typelogic

A const int varcan be dynamically set to a value at runtime and once it is set to that value, it can no longer be changed.

Aconst int var可以在运行时动态设置为一个值,一旦设置为该值,就不能再更改。

A constexpr int varcannot be dynamically set at runtime, but rather, at compile time. And once it is set to that value, it can no longer be changed.

Aconstexpr int var不能在运行时动态设置,而是在编译时动态设置。一旦设置为该值,就无法再更改。

Here is a solid example:

这是一个可靠的例子:

int main(int argc, char*argv[]) {
    const int p = argc; 
    // p = 69; // cannot change p because it is a const
    // constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time 
    constexpr int r = 2^3; // this works!
    // r = 42; // same as const too, it cannot be changed
}

The snippet above compiles fine and I have commented out those that cause it to error.

上面的代码片段编译得很好,我已经注释掉了那些导致它出错的代码。

The key notions here to take note of, are the notions of compile timeand run time. New innovations have been introduced into C++ intended to as much as possible ** know **certain things at compilation time to improve performance at runtime.

这里的关键概念要注意的,是的概念compile timerun time。C++ 中引入了新的创新,旨在尽可能多地** know **在编译时提高运行时的性能。

回答by Lewis Kelsey

I don't think any of the answers really make it clear exactly what side effects it has, or indeed, what it is.

我认为任何答案都没有真正说明它有什么副作用,或者确实是什么。

constexprand constat namespace/file-scope are identical when initialised with a literal or expression; but with a function, constcan be initialised by any function, but constexprinitialised by a non-constexpr function will generate a compiler error. Both constexprand constare implicitly internal linkage for variables (well actually, they don't survive to get to the linking stage if compiling -O1 and stronger, and staticdoesn't force the compiler to emit an internal (local) linker symbol for constor constexprwhen at -O1 or stronger; the only time it does this is if you take the address of the variable. constand constexprwill be an internal symbol unless expressed with externi.e. extern constexpr/const int i = 3;needs to be used). On a function, constexprmakes the function permanently never reach the linking stage (regardless of externor inlinein the definition or -O0 or -Ofast), whereas constnever does, and staticand inlineonly have this effect on -O1 and above. When a const/constexprvariable is initialised by a constexprfunction, the load is always optimised out with any optimisation flag, but it is never optimised out if the function is only staticor inline, or if the variable is not a const/constexpr.

constexprconst在名字空间/文件范围时用文字或表达初始化是相同的; 但是对于一个函数,const可以由任何函数constexpr初始化,但由非 constexpr 函数初始化将产生编译器错误。这两个constexprconst是变量隐含内部链接(当然实际上,他们根本无法生存,如果编译-O1做强才能到连接阶段,并static没有强制编译器发出内部(本地)连接符号constconstexpr在当-O1或更强;它这样做的唯一的时间是,如果你把变量的地址。constconstexpr将是一个内部符号除非表示与externextern constexpr/const int i = 3;需要使用)。在一个函数上,constexpr使功能永久不会到达连接阶段(无论externinline在定义或-O0或-Ofast),而const从来不,并且staticinline只对-O1和上述这种效果。当一个const/constexpr变量被一个constexpr函数初始化时,加载总是用任何优化标志优化出来,但如果函数只是staticinline,或者如果变量不是const/ ,它永远不会优化出来constexpr

Standard compilation (-O0)

标准编译 (-O0)

#include<iostream>
constexpr int multiply (int x, int y)
{

  return x * y;
}

extern const int val = multiply(10,10);
int main () {
  std::cout << val;
} 

compiles to

编译为

val:
        .long   100  //extra external definition supplied due to extern

main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 100 //substituted in as an immediate
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 

However

然而

#include<iostream>
const int multiply (int x, int y)
{

  return x * y;
}

const int val = multiply(10,10); //constexpr is an error
int main () {
  std::cout << val;
}

Compiles to

编译为

multiply(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, DWORD PTR [rbp-8]
        pop     rbp
        ret

main:
        push    rbp
        mov     rbp, rsp
        mov     eax, DWORD PTR val[rip]
        mov     esi, eax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 
        mov     esi, 10
        mov     edi, 10
        call    multiply(int, int)
        mov     DWORD PTR val[rip], eax

This clearly shows that constexprcauses the initialisation of the const/constexprfile-scope variable to occur at compile time and produce no global symbol, whereas not using it causes initialisation to occur before mainat runtime.

这清楚地表明这constexpr会导致const/constexpr文件范围变量的初始化在编译时发生并且不产生全局符号,而不使用它会导致main在运行时之前发生初始化。

Compiling using -Ofast

使用 -Ofast 编译

Even -Ofast doesn't optimise out the load! https://godbolt.org/z/r-mhif, so you needconstexpr

即使 -Ofast 也不会优化负载!https://godbolt.org/z/r-mhif,所以你需要constexpr



constexprfunctions can also be called from inside other constexprfunctions for the same result. constexpron a function also prevents use of anything that can't be done at compile time in the function; for instance, a call to the <<operator on std::cout.

constexpr函数也可以从其他constexpr函数内部调用以获得相同的结果。constexpr在函数上还可以防止在函数中使用在编译时无法完成的任何事情;例如,在 上呼叫<<操作员std::cout

constexprat block scope behaves the same in that it produces an error if initialised by a non-constexpr function; the value is also substituted in immediately.

constexprat 块作用域的行为相同,如果由非 constexpr 函数初始化,则会产生错误;该值也立即被替换。

In the end, its main purpose is like C's inline function, but it is only effective when the function is used to initialise file-scope variables (which functions cannot do on C, but they can on C++ because it allows dynamic initialisation of file-scope variables), except the function cannot export a global/local symbol to the linker as well, even using extern/static, which you could with inlineon C; block-scope variable assignment functions can be inlined simply using an -O1 optimisation without constexpron C and C++.

最后,它的主要目的就像 C 的内联函数一样,但只有在该函数用于初始化文件范围变量时才有效(这些函数在 C 上不能做,但在 C++ 上可以,因为它允许动态初始化文件-范围变量),除了该函数也不能将全局/局部符号导出到链接器,即使使用extern/static,您也可以inline在 C 上使用;块范围变量赋值函数可以简单地使用 -O1 优化来内联,而不需要constexpr在 C 和 C++ 上。

回答by Subhash Malireddy

First of all, both are qualifiers in c++. A variable declared const must be initialized and cannot be changed in the future. Hence generally a variable declared as a const will have a value even before compiling.

首先,两者都是 C++ 中的限定符。声明为 const 的变量必须被初始化并且以后不能更改。因此,通常声明为 const 的变量甚至在编译之前就有一个值。

But, for constexpr it is a bit different.

但是,对于 constexpr,它有点不同。

For constexpr, you can give an expression that could be evaluated during the compilation of the program.

对于 constexpr,您可以给出一个可以在程序编译期间计算的表达式。

Obviously, the variable declared as constexper cannot be changed in the future just like const.

显然,声明为 constexper 的变量在以后不能像 const 一样改变。