【问题标题】:Difference between `constexpr` and `const``constexpr` 和 `const` 之间的区别
【发布时间】:2012-12-16 11:07:03
【问题描述】:

constexprconst 有什么区别?

  • 什么时候可以只使用其中一个?
  • 我什么时候可以同时使用,我应该如何选择一个?

【问题讨论】:

  • constexpr 创建一个编译时常量; const 仅表示该值无法更改。
  • 可能来自boost/hana 库的这篇文章可以启发constexpr 的一些问题,您可以在哪些地方使用constexpr,而在哪些地方不能:boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
  • @0x499602D2 "只表示值不能改变" 对于用字面量初始化的标量,不能改变的值也是编译时间常数。
  • @curiousguy 是的,我的评论过于简单化了。诚然,我当时也是constexpr 的新手 :)

标签: c++ c++11 constants constexpr


【解决方案1】:

基本含义和语法

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

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

  • constexpr 声明一个对象适合在标准所称的常量表达式中使用。但请注意,constexpr 并不是唯一的方法。

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

  • const 只能用于非静态成员函数,不能用于一般函数。它保证成员函数不会修改任何非静态数据成员(可变数据成员除外,无论如何都可以修改)。

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

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

常量表达式

如上所述,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

那么我什么时候必须使用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
     }
    

我什么时候可以/应该同时使用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 被声明为地址常量表达式,即本身就是常量表达式的指针。 (通过将地址运算符应用于静态/全局常量表达式来生成地址时,这是可能的。)这里,constexprconst 都是必需的:constexpr 总是指正在声明的表达式(这里 @987654357 @),而const 指的是int(它声明了一个指向常量的指针)。删除 const 会使表达式非法(因为 (a) 指向非常量对象的指针不能是常量表达式,并且 (b) &amp;N 实际上是指向常量的指针)。

B.在成员函数声明中。 在 C++11 中,constexpr 意味着 const,而在 C++14 和 C++17 中则不是这样。 C++11下声明的成员函数为

constexpr void f();

需要声明为

constexpr void f() const;

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

【讨论】:

  • IMO “不一定在编译时评估”比将它们视为“在编译时评估”的帮助要小。常量表达式的约束意味着编译器对其求值相对容易。如果不满足这些约束,编译器必须抱怨。由于没有副作用,您永远无法区分编译器是否“评估”它。
  • @aschepler 当然。我的主要观点是,如果您在非常量表达式上调用 constexpr 函数,例如一个普通变量,这是完全合法的,并且该函数将像任何其他函数一样使用。它不会在编译时评估(因为它不能)。也许您认为这很明显——但如果我声明声明为 constexpr 的函数将始终在编译时进行评估,那么它可能会以错误的方式解释。
  • 是的,我说的是constexpr 对象,而不是函数。我喜欢将对象上的constexpr 视为强制对值进行编译时评估,而将函数上的constexpr 视为允许在编译时或运行时适当地评估函数。
  • 更正:'const'只是一个限制,你不能改变一个变量的值;它不承诺该值不会改变(即,由其他人)。它是一个写属性,而不是一个读属性。
  • 这句话:保证成员函数不会修改任何非静态数据成员。遗漏了一个重要细节。标记为mutable 的成员也可以被const 成员函数修改。
【解决方案2】:

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

constexpr 告诉编译器这个表达式会产生一个编译时间常数值,所以它可以用在数组长度之类的地方,赋值给const变量等。Oli 给出的link 有很多很好的例子。

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

【讨论】:

  • const & constexpr 用法,例如:en.cppreference.com/w/cpp/container/array/get
  • @ManoharReddyPoreddy 我认为en.cppreference.com/w/cpp/container/array/begin 是一个更好的例子,它具有constexpr T f(x) const 的签名,两者都适用于函数,而在constexpr const T f(x)(即数组::get 签名)中const 是返回类型的一部分,而不是函数属性的一部分(不确定这个的标准名称)。尽管此 awnser 无法确认在成员函数上使用了 const
  • @ted 我将我的编程语言从 C++ 更改为 javascript 有一段时间了,所以我几乎不记得上面发布的内容:),因此出于同样的原因无法发表评论。
【解决方案3】:

概述

  • 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 让您可以定义真正的常量表达式,这些表达式对于时间关键型应用程序、系统编程、模板以及一般而言至关重要, 在任何依赖于编译时常量的代码中。

常量表达式函数

常量表达式函数是声明为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)

常量表达式对象

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

一个常量表达式对象的行为就好像它被声明为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

常量表达式构造函数

常量表达式构造函数是声明为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 所著的Effective Modern C++一书中关于constexpr的提示:

  • constexpr 对象是 const 并使用编译期间已知的值进行初始化;
  • constexpr 函数在使用编译期间已知的参数调用时会产生编译时结果;
  • 与非constexpr 对象和函数相比,constexpr 对象和函数可以在更广泛的上下文中使用;
  • constexpr 是对象或函数接口的一部分。

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

【讨论】:

  • 感谢展示不同情况的出色示例代码。与其他一些解释一样好,我发现看到实际代码更加有用和易于理解。它确实有助于巩固我对正在发生的事情的理解。
  • 谢谢,这比选择的答案更能帮助我理解。
【解决方案4】:

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.

所以const 变量可以定义编译时常量(如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.

【讨论】:

  • 另外,在 C 中,constexpr int 存在,但拼写为 const int
【解决方案5】:

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

【讨论】:

  • 仍有性能问题。似乎 constexpr 函数如果在运行时评估可能比函数的非 constexpr 版本慢。此外,如果我们有一个常量值,我们应该更喜欢“const”还是“constexpr”? (更多样式问题生成的程序集看起来一样)
【解决方案6】:

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
}

上面的 sn-p 编译得很好,我已经注释掉了导致它出错的那些。

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

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

【讨论】:

    【解决方案7】:

    正如@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
    

    【讨论】:

      【解决方案8】:

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

      constexprconst 在命名空间/文件范围内使用文字或表达式初始化时是相同的;但是对于函数,const 可以由任何函数初始化,但由非 constexpr(未用 constexpr 或非 constexpr 表达式标记的函数)初始化的 constexpr 将生成编译器错误。 constexprconst 都是变量的隐式内部链接(实际上,如果编译 -O1 或更强,它们将无法进入链接阶段,并且 static 不会强制编译器发出内部constconstexpr 在 -O1 或更高时的(本地)链接器符号;唯一的情况是,如果您获取变量的地址。constconstexpr 将是内部符号,除非被表达与externextern constexpr/const int i = 3; 需要使用)。在函数上,constexpr 使函数永远不会到达链接阶段(无论定义中的 externinline 还是 -O0 或 -Ofast),而 const 永远不会,而 static 和 @ 987654340@ 仅对 -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 文件范围变量的初始化在编译时发生并且不产生全局符号,而不使用它会导致在运行时在 main 之前发生初始化。

      使用 -Ofast 编译

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


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

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

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

      【讨论】:

      • 链接器上的好点。一般来说,使用 constexpr 是否会被认为更安全,因为它会减少符号泄漏?
      • @NeilMcGill 并不是真的,因为如果使用 -O1 或更高版本进行编译,则 inline 和 static 将导致编译器不会发出用于乘法的本地符号。 Constexpr 是唯一优化 val 负载的方法,但除此之外,它与将 static 或 inline 放在函数之前相同。我也忘记了别的东西。 constexpr 是唯一一个不为 -O0 上的函数发出符号的关键字,静态和内联 do
      【解决方案9】:

      const 和 constexpr 关键字概述

      在 C++ 中,如果一个 const 对象是用一个常量表达式初始化的,我们可以在任何需要常量表达式的地方使用我们的 const 对象。

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

      比如我们可以在switch中做一个case语句。

      constexpr 可以与数组一起使用。

      constexpr 不是类型。

      constexpr 关键字可以与 auto 关键字结合使用。

      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;
      

      Consexpr 函数

      通常,函数的返回值是在运行时获取的。 但是当满足某些条件时,对 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”和“integral types”。

      如果我们使用 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
       };
      

      【讨论】:

      • “如果一个 const 对象用常量表达式初始化,我们可以在需要常量表达式的任何地方使用我们的 const 对象。” 仅适用于整型常量。
      • 感谢@HolyBlackCat 提供的信息
      【解决方案10】:

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

      但是,对于 constexpr 来说有点不同。

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

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

      【讨论】:

        猜你喜欢
        • 2013-08-11
        • 2018-11-05
        相关资源
        最近更新 更多