constexpr和const之间的区别是什么?

发布于 2025-01-25 10:27:04 字数 125 浏览 3 评论 0原文

constexprconst之间有什么区别?

  • 我什么时候只能使用其中一个?
  • 我什么时候可以使用两者,如何选择一个?

What's the difference between constexpr and const?

  • When can I use only one of them?
  • When can I use both and how should I choose one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

情感失落者 2025-02-01 10:27:04

基本含义和语法

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

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

  • constexpr将对象声明为适合在标准称为常数表达式中使用的对象。但是请注意,constexpr不是这样做的唯一方法。

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

  • const只能用于非静态成员函数,而不是通常的功能。它可以保证该成员函数不会修改任何非静态数据成员(可突变的数据成员除外,无论如何都可以修改)。

  • constexpr可以与成员和非成员功能以及构造函数一起使用。它声明适合在常数表达式中使用的函数。仅当功能符合某些标准(7.1.5/3,4)时,编译器才会接受,最重要的是(†)

    • 功能主体必须是非虚拟的,并且非常简单:除了typedef和静态断言外,只允许单个返回语句。在构造函数的情况下,只允许使用初始化列表,Typedefs和静态断言。 (=默认= delete也允许。)
    • 从C ++ 14开始,规则更加放松,此后允许在ConstexPR函数中允许:asm声明,goto语句,带有的语句除case默认,try-block,非文字类型的变量的定义,静态或线程存储持续变量的定义,定义以外的标签。未执行初始化的变量。
    • 参数和返回类型必须为 字面类型(即一般而言,非常简单的类型,通常是标量或聚合)


的常数表达式

如上所述,constexpr将两个对象声明为以及适合在恒定表达式中使用的功能。恒定表达式不仅是恒定的:

  • 可以在需要编译时评估的地方使用,例如,模板参数和数组大小的指定器:

     模板< int n>
    fixed_size_list类
    {/*....*/};
    
    fixe_size_list< x> mylist; // x必须是整数常数表达式
    
    int数字[x]; // x必须是整数常数表达式
     
  • ,但请注意:

  • 将某些内容声明为constexpr并不一定保证将在编译时对其进行评估。它可以用于此类,但也可以在运行时进行评估的其他地方使用。

  • 一个对象可能适合在不声明constexpr的情况下使用常量表达式。示例:

      int main()
    {
      const int n = 3;
      int数字[n] = {1,2,3}; // n是恒定的表达
    }
     

    这是可能的,因为n在声明时间持续和初始化,以字面的方式满足恒定表达式的条件,即使未声明constexpr, 。

因此,我什么时候实际上必须使用constexpr

  • object n可以用作常量表达式< em>没有声明 constexpr。对于所有对象都是如此:
    • const
    • 积分或枚举类型
    • 在声明时间初始化的表达式本身就是恒定表达式。

[这是由于§5.19/2造成的:恒定表达式不得包含涉及“ lvalue-rvalue修改的子表达,除非[…]积分或枚举类型的glvalue [… ]“感谢理查德·史密斯(Richard Smith)纠正了我较早的主张,即所有字面类型都是如此。]

  • 对于函数适合在恒定表达式中使用,它必须明确声明constexpr;仅仅满足恒定表达函数的标准是不够的。示例:

     模板&lt; int n&gt;
    班级列表
    {};
    
    constexpr int sqr1(int arg)
    {返回arg * arg; }
    
    int sqr2(int arg)
    {返回arg * arg; }
    
    int main()
    {
      const int x = 2;
      列表&lt; sqr1(x)&gt; mylist1; //确定:SQR1是constexpr
      列表&lt; sqr2(x)&gt; mylist2; //错误:SQR2不是constexpr
    }
     

何时可以/应该同时使用constconstexpr 一起?

a。在对象声明中。当两个关键字都涉及要声明的同一对象时,这是不必要的。 constexpr暗示const

constexpr const int N = 5;

与之相同

constexpr int N = 5;

,请注意,在每个关键字都参考声明的不同部分时可能会有某些情况:

static constexpr int N = 3;

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

在此,np声明为地址常数表达,即指针本身就是一个指针恒定表达。 (这是可以通过将地址运算符应用于静态/全局常数表达式生成地址时的。 始终是指声明的表达式(此处np),而const是指int(它声明了指针到 - const)。删除const将使表达式非法(因为(a)指向非const对象的指针不能是恒定的表达式,并且(b)&amp; n在 - 将指针到恒定构成)。

b。在成员函数声明中。在C ++ 11中,constexpr暗示const,而在C ++ 14和C ++ 17中,情况并非如此。 C ++下的成员函数,

constexpr void f();

C ++ 11下声明为

constexpr void f() const;

以便仍然可以用作const函数。

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. 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. But note that constexpr is not the only way to do this.

When applied to functions the basic difference is this:

  • const can 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 (except for mutable data members, which can be modified anyway).

  • constexpr can 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 return statement is allowed. In the case of a constructor, only an initialization list, typedefs, and static assert are allowed. (= default and = delete are allowed, too, though.)
    • 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, the definition of a variable of non-literal type, definition of a variable of static or thread storage duration, the 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)

Constant expressions

As said above, constexpr declares both objects as well as functions as fit for use in constant expressions. 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. 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. 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.

So when do I actually have to use constexpr?

  • An object like N above can be used as constant expression without being declared constexpr. This is true for all objects that are:
    • const and
    • 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 subexpression 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 function to be fit for use in constant expressions, it must be 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
    }
    

When can I / should I use both, const and constexpr together?

A. In object declarations. This is never necessary when both keywords refer to the same object to be declared. constexpr implies 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, i.e. a 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 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). 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).

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

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.

浅笑轻吟梦一曲 2025-02-01 10:27:04

const适用于变量可防止它们在代码中被修改

constexpr告诉编译器,此表达式导致编译时间常数值,因此可以在诸如数组长度之类的地方使用,分配给<<<<<代码> const 变量等。 >由Oli给出,有很多很好的例子。

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

const applies for variables, and prevents them from being modified in your code.

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.

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

猫九 2025-02-01 10:27:04

概述

  • const保证程序不会更改对象的值。但是,const不能保证对象的初始化类型。

    考虑:

      const int mx = numeric_limits&lt; int&gt; :: max(); //确定:运行时初始化
     

    函数max()仅返回字面价值。但是,由于初始化器是函数调用,因此mx经历运行时初始化。因此,您不能将其用作常数表达式

      int arr [mx]; //错误:“需要恒定表达式”
     
  • constexpr是一个新的C ++ 11关键字,它消除了您需要创建宏和硬编码文字的需要。它还保证在某些条件下对象经历静态初始化。它控制表达式的评估时间。通过执行编译其表达式评估constexpr允许您定义True 常数表达式,这些表达式对于时间关键应用程序,系统编程,,系统编程,模板以及一般来说,在任何依赖编译时常数的代码中。

常数表达函数

a 常数表达函数是声明constexpr的函数。它的身体必须是非虚拟的,并且仅由单个返回语句组成,除了typedef和静态断言。它的论点和返回价值必须具有字面类型。它可以与非恒定表达参数一起使用,但是完成后,结果不是恒定的表达式。

恒定表达函数旨在替换硬编码文字而无需牺牲性能或类型的安全性。

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)

常数表达对象

a 常数表达对象是声明constexpr的对象。必须以恒定表达式或由具有恒定表达参数的恒定表达构造器构建的rvalue初始化。

恒定表达对象的行为就像是被声明为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

常数表达构造函数

A 常量表达构造函数是声明constexpr的构造函数。它可以具有成员初始化列表,但是除了Typedef和静态断言外,它的身体必须是空的。它的论点必须具有字面类型。

恒定表达构造函数允许编译器在编译时初始化对象,前提是构造函数的参数都是恒定的表达式。

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

斯科特·迈耶斯(Scott Meyers)的《有效的现代C ++ 》一书中的提示有关constexpr

  • constexpr对象是const的,并在编译过程中以已知的值初始化;
  • constexpr函数在与汇编过程中已知值的参数调用时会产生编译时间结果;
  • constexpr对象和函数可在更广泛的上下文范围内使用,而不是非constexpr对象和函数;
  • constexpr是对象或函数接口的一部分。

来源:
”>使用constexpr来提高安全性,性能并封装在C ++中

Overview

  • const guarantees that a program does not change an object’s value. However, const does 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, mx undergoes runtime initialization. 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. 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.

Constant-expression functions

A constant-expression function is 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.

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

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:

  • constexpr objects are const and are initialized with values known during compilation;
  • constexpr functions produce compile-time results when called with arguments whose values are known during compilation;
  • constexpr objects and functions may be used in a wider range of contexts than non-constexpr objects and functions;
  • constexpr is part of an object’s or function’s interface.

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

风吹短裙飘 2025-02-01 10:27:04

constconstexpr都可以应用于变量和函数。即使它们彼此相似,实际上它们是非常不同的概念。

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.

constconstexpr之间的主要区别是已知其初始化值的时间(评估)。虽然可以在编译时间和运行时评估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.

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

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

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

因此,这意味着:

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 const变量可以定义 compile time常数喜欢size1,可用于指定数组大小和运行时常数喜欢size2仅在运行时已知,不能用于定义数组大小。另一方面,constexpr始终定义可以指定数组大小的编译时间常数。

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.
   }
};

constexpr是一个不同的概念。它将函数(成员或非会员)标记为可以在编译时间评估的函数,如果编译时间常数作为其参数传递。例如,您可以写这篇文章。

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.

顺便说一句,即使传递了非恒定参数,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.

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

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

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


    }
};

一个更“疯狂”的样本。

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.

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

Both const and constexpr mean that their values can't be changed after their initialization. So for example:

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 const and constexpr is the time when their initialization values are known (evaluated). While the values of const variables can be evaluated at both compile time and runtime, constexpr are always evaluated at compile time. For example:

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.

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 const variables can define both compile time constants like size1 that can be used to specify array sizes and runtime constants like size2 that are known only at runtime and can't be used to define array sizes. On the other hand constexpr always define compile time constants that can specify array sizes.

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

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 constexpr is 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.

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 constexpr functions 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.

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 constexpr can be also applied to the member functions (methods), operators and even constructors. For instance.

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.
聽兲甴掵 2025-02-01 10:27:04

根据Bjarne Stroustrup的“ C ++编程语言4th Editon”的书
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

使一个函数在恒定表达式中可用,即在将评估的表达式中
编译器必须定义 constexpr
例如:

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

为了成为constexpr,一个函数必须相当简单:只是计算值的返回statement。一个
constexpr函数可用于非恒定参数,但是完成后,结果不是
恒定表达。我们允许使用非恒定表达参数调用constexpr函数
在不需要恒定表达的环境中
相同的功能两次:一次用于常数表达式,一次用于变量。
在少数地方,语言规则需要恒定表达(例如,数组界限(§2.2.5,
§7.3),案例标签(§2.2.4,§9.4.2),一些模板参数(§25.2),并宣布使用常数。
constexpr)。在其他情况下,编译时间评估对于性能很重要。独立于
绩效问题,不变性的概念(具有不变状态的对象)是
重要的设计关注(§10.4)。

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:

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

const int var可以在运行时动态设置为一个值,一旦将其设置为该值,就无法将其更改。

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

这是一个扎实的示例:

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
}

上面的片段填充了,我已经评论了那些导致错误的人。

这里要注意的关键概念是编译时间运行时间的概念。已将新的创新引入了C ++中,旨在尽可能多地**知道**在编译时某些事情以提高运行时的性能。

任何不涉及上述两个关键概念的解释尝试都是幻觉。

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

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

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 time and 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.

Any attempt of explanation which does not involve the two key notions above, is hallucination.

℡Ms空城旧梦 2025-02-01 10:27:04

正如 @0x499602d2已经指出的那样,const仅确保在初始化后不能更改值,而该值在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

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.
Consider the following example(from 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
巷雨优美回忆 2025-02-01 10:27:04

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

constexprconstem在命名空间/文件范围内使用文字或表达式初始化时相同;但是有了一个函数,const可以通过任何函数初始化,但是constexpr由non-constexpr初始化(该函数未标记为constexpr或non constexpr Expression )将生成编译器错误。 constexprconst都是变量隐含的内部链接(实际上,如果编译-O1和更强,它们无法生存到链接阶段,并且静态不强迫编译器发射constconstexpr时,在-O1或更强的情况下发出了内部(本地)链接器符号;这是您使用变量的地址。 /const int i = 3;需要使用)。在函数上,constexpr使该函数永久到达链接阶段(无论extern extern 或inline在定义或-O0或-OFAST中),而const永远不会做,staticinline仅在-O1及更高版本上具有此影响。当const/constexpr变量由constexpr函数初始化时,始终使用任何优化标志优化了加载,但绝不是优化的如果该函数仅为staticinline,或者该变量不是const/constexpr

标准汇编(-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;
} 

编译以编译

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

清楚

#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;
}

为此

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

地表明,constexpr导致const/constexpr file-scope变量的初始化to在编译时间发生,没有产生全局符号,而不使用它会导致初始化发生在运行时main之前。

使用-ofast

甚至 - 福特均无法优化负载! https://godbolt.org/z/r-mhif ,所以您需要需要< /em> constexpr


constexpr函数也可以从其他constexpr函数中调用相同结果。 constexpr在功能上还可以防止在函数中编译时间无法完成的任何操作;例如,在std :: Cout上呼叫&lt;&lt;&lt;&lt;&lt;

constexpr在块范围处的行为相同,因为如果由非constexpr函数初始化,则会产生错误;该值也可以立即替换。

最后,它的主要目的就像C的内联函数一样,但是只有使用该函数来初始化文件范围变量(哪些函数不能在C上执行,但是它们可以在C ++上,因为它允许动态文件 - 范围变量),除了该函数也无法将全局/本地符号导出到链接器,即使使用extern/static,您也可以在c上使用inline;可以简单地使用constexpr在C和C ++上使用-O1优化的块范围变量分配函数。

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

constexpr and const at namespace/file-scope are identical when initialised with a literal or expression; but with a function, const can be initialised by any function, but constexpr initialised by a non-constexpr (a function that isn't marked with constexpr or a non constexpr expression) will generate a compiler error. Both constexpr and const are implicitly internal linkage for variables (well actually, they don't survive to get to the linking stage if compiling -O1 and stronger, and static doesn't force the compiler to emit an internal (local) linker symbol for const or constexpr when at -O1 or stronger; the only time it does this is if you take the address of the variable. const and constexpr will be an internal symbol unless expressed with extern i.e. extern constexpr/const int i = 3; needs to be used). On a function, constexpr makes the function permanently never reach the linking stage (regardless of extern or inline in the definition or -O0 or -Ofast), whereas const never does, and static and inline only have this effect on -O1 and above. When a const/constexpr variable is initialised by a constexpr function, the load is always optimised out with any optimisation flag, but it is never optimised out if the function is only static or inline, or if the variable is not a const/constexpr.

Standard compilation (-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 constexpr causes the initialisation of the const/constexpr file-scope variable to occur at compile time and produce no global symbol, whereas not using it causes initialisation to occur before main at runtime.

Compiling using -Ofast

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


constexpr functions can also be called from inside other constexpr functions for the same result. constexpr on 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 at 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.

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 inline on C; block-scope variable assignment functions can be inlined simply using an -O1 optimisation without constexpr on C and C++.

此岸叶落 2025-02-01 10:27:04

C ++中const和ConstexPR关键字的概述

,如果使用常数表达式初始化const对象,则可以在需要恒定表达式的情况下使用我们的const对象。

const int x = 10;
int a[x] = {0};

例如,我们可以在开关中做一个案例语句。

constexpr可以与数组一起使用。

constexpr不是类型。

constexpr关键字可以与自动关键字一起使用。

constexpr auto x = 10;

struct Data {   // We can make a bit field element of struct.   
    int a:x;
 };

如果我们以常数表达式初始化const对象,则该const对象生成的表达式现在也是常数表达式。

<强>恒定表达式:一个表达式,其值可以在编译时计算。

x*5-4 //这是一个常数表达式。 对于编译器,键入此表达式和直接键入46之间没有区别。

初始化是必须的。它只能用于阅读目的。它不能更改。到目前为止,“ const”和“ constexpr”关键字之间没有区别。

注意:我们可以在同一声明中使用constexpr和const。

constexpr const int* p;

constexpr函数

通常,在运行时获得函数的返回值。
但是,在满足某些条件时,将获得对ConstexPR函数

的 调用。所有参数变量如果有多个参数,则如果CE将在编译时间中计算函数的返回值。 !!!!

constexpr int square (int a){
return a*a;
}

constexpr int a = 3;
constexpr int b = 5;

int arr[square(a*b+20)] = {0}; //This expression is equal to int arr[35] = {0};

为了使函数成为constexpr函数,该函数的返回值类型和函数参数的类型必须在类型类别中,称为“文字类型”。

constexpr函数是隐式内联函数。

一个重要的一点:

不需要以恒定表达式调用constexpr函数。它不是强制性的。如果发生这种情况,则计算将在编译时完成。它将被视为普通功能调用。因此,在需要恒定表达式的情况下,我们将不再能够使用此表达式。

作为constexpr函数所需的条件如下所示;

1)函数参数中使用的类型和函数的返回值的类型必须是字面类型。

2)在功能内部不应使用具有静态寿命的局部变量。

3)如果该函数是合法的,则在编译时间中以恒定表达式调用此函数时,编译器会在编译时间中计算函数的返回值。

4)编译器需要查看该函数的代码,因此ConstexPR函数几乎总是在标题文件中。

5)为了使我们创建为constexpr函数的函数,该函数的定义必须在标题文件中。

奖励

通常具有默认会员初始化,可以在类中初始化具有const和积分类型的静态数据成员。但是,为了做到这一点,必须同时有“ const”和“积分类型”。

如果我们使用static constexpr,那么它不必是在类中初始化它的积分类型。只要我以恒定的表达初始化它,就没有问题。

class Myclass  {
         const static int sx = 15;         // OK
         constexpr static int sy = 15;     // OK
         const static double sd = 1.5;     // ERROR
         constexpr static double sd = 1.5; // OK
 };

An overview of the const and constexpr keywords

In C ++, if a const object is initialized with a constant expression, we can use our const object wherever a constant expression is required.

const int x = 10;
int a[x] = {0};

For example, we can make a case statement in switch.

constexpr can be used with arrays.

constexpr is not a type.

The constexpr keyword can be used in conjunction with the auto keyword.

constexpr auto x = 10;

struct Data {   // We can make a bit field element of struct.   
    int a:x;
 };

If we initialize a const object with a constant expression, the expression generated by that const object is now a constant expression as well.

Constant Expression : An expression whose value can be calculated at compile time.

x*5-4 // This is a constant expression. For the compiler, there is no difference between typing this expression and typing 46 directly.

Initialize is mandatory. It can be used for reading purposes only. It cannot be changed. Up to this point, there is no difference between the "const" and "constexpr" keywords.

NOTE: We can use constexpr and const in the same declaration.

constexpr const int* p;

Constexpr Functions

Normally, the return value of a function is obtained at runtime.
But calls to constexpr functions will be obtained as a constant in compile time when certain conditions are met.

NOTE : Arguments sent to the parameter variable of the function in function calls or to all parameter variables if there is more than one parameter, if C.E the return value of the function will be calculated in compile time. !!!

constexpr int square (int a){
return a*a;
}

constexpr int a = 3;
constexpr int b = 5;

int arr[square(a*b+20)] = {0}; //This expression is equal to int arr[35] = {0};

In order for a function to be a constexpr function, the return value type of the function and the type of the function's parameters must be in the type category called "literal type".

The constexpr functions are implicitly inline functions.

An important point :

None of the constexpr functions need to be called with a constant expression.It is not mandatory. If this happens, the computation will not be done at compile time. It will be treated like a normal function call. Therefore, where the constant expression is required, we will no longer be able to use this expression.

The conditions required to be a constexpr function are shown below;

1 ) The types used in the parameters of the function and the type of the return value of the function must be literal type.

2 ) A local variable with static life time should not be used inside the function.

3 ) If the function is legal, when we call this function with a constant expression in compile time, the compiler calculates the return value of the function in compile time.

4 ) The compiler needs to see the code of the function, so constexpr functions will almost always be in the header files.

5 ) In order for the function we created to be a constexpr function, the definition of the function must be in the header file.Thus, whichever source file includes that header file will see the function definition.

Bonus

Normally with Default Member Initialization, static data members with const and integral types can be initialized within the class. However, in order to do this, there must be both "const" and "integral types".

If we use static constexpr then it doesn't have to be an integral type to initialize it inside the class. As long as I initialize it with a constant expression, there is no problem.

class Myclass  {
         const static int sx = 15;         // OK
         constexpr static int sy = 15;     // OK
         const static double sd = 1.5;     // ERROR
         constexpr static double sd = 1.5; // OK
 };
微凉 2025-02-01 10:27:04

首先,两者都是C ++的预选赛。
声明的const的变量必须初始化,并且将来不能更改。
因此,通常甚至在编译之前,称为const的变量也将具有一个值。

但是,对于constexpr而言,这有点不同。

对于ConstexPR,您可以提供可以在程序编译期间进行评估的表达式。

显然,将来像const一样将无法更改为constexper的变量。

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.

But, for constexpr it is a bit different.

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文