天天看点

constexpr和const之间的区别

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

What's the difference between

constexpr

and

const

?

constexpr

const

什么区别?
  • 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

      语句,带有除

      case

      default

      之外的标签的语句,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: 它可以在需要编译时评估的地方使用,例如模板参数和数组大小说明符:
  • 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: 例:
    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: 例:

When can I / should I use both,

const

and

constexpr

together?

我什么时候可以同时使用

const

constexpr

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).

(当通过将地址运算符应用于静态/全局常量表达式来生成地址时,这是可能的。)在这里,

constexpr

const

都是必需的:

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: 考虑: 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 : 因此,不能将其用作常量表达式 :
  • 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
           

继续阅读