constexpr in C++

 You must have read the post about const pointer and pointer to const or you can learn after this also.

const (run time)

  • It is part of data type declaration
  • It establishes that initially assigned value can't be changed. 

For example, const int is as same as data type int with an additional restriction declaring that the value of int can't be changed. 
  • It is like an agreement that the code which is under const will not change the data.
  • Can't be evaluated at compile time rather run-time. But, C++98 const int will be evaluated at compile time. 

Eg1: const int var1 = 5; // will be evaluated at compile-time

Eg2: const double var2 = 5.6; // will not be evaluated at compile-time

With C++11 and later, a top level const on a return value disables move semantics which can lead to otherwise needless copying, so the new rule with C++11 and later is 'almost never use const on return type'. 

That's an important exception to the general rule of using const almost everywhere. 

#include <iostream>
#include <memory>

int main()
{
    const int var1 = 5;
    const double var2 = 8.4;
    std::cout << "var1 " << var1 
              << "   var2 " << var2 <<std::endl;
    constexpr int var3 = var1 + 3;
    constexpr double var4 = var2 + 3.5;
    std::cout << "var3 " << var3 
              << "   var4 " << var4 <<std::endl;

    return 1;
}

Output:

1
2
<source>: In function 'int main()':
<source>:11:36: error: the value of 'var2' is not usable in a constant expression
   11 |     constexpr double var4 = var2 + 3.5;
      |                                    ^~~
<source>:7:18: note: 'var2' was not declared 'constexpr'
    7 |     const double var2 = 8.4;
      |                  ^~~~

Click here for compiler explorer

const variable        -      const int var;
const reference      -      const int& var;
const pointer          -      int * const;
Pointer to const      -      int const *;
const method          -     int function() const {};

constexpr (compile-time)

  • It is not part of data type declaration unlike const, so it can appear only once in the declaration.
  • It is similar to static and inline keywords. So, It acts as a modifier to the entire declaration. 
  • It 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 expressionconstexpr 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. It is not a rule and not necessary to evaluate the code at compile-time. 

If you see it on the right-hand side, the factorial resulted '24' during compilation in line 4. 

Click here to debug this program in Compiler Explorer.
  • Two constexpr on a single expression is not possible, but const is possible.

#include <iostream>

using namespace std;

constexpr int fac(constexpr int n)
{
    return n == 1 ? 1 : n * fac(n - 1);
}

int main()
{
    constexpr int f5 = fac(5);

    cout << "The value of f5 is " << f5 << endl;
}

Output:

1
2
main.cpp:5:19: error: a parameter cannot be declared constexpr
    5 | constexpr int fac(constexpr int n)

  • This mostly accompanied by static keyword for class members. 

static constexpr double PI = 3.1415926;

  • A function or variable both can be specified as constexpr. constexpr function can be called by another constexpr function and not simple function. The return value of constexpr function can be assigned to another const variable. 
  • The return value of constexpr function  is constant and computed at compile time. 
  • prefix increment is not allowed. 
const constexpr int var  equals to constexpr const int var

Advantages

  • It is mainly used for performance improvement. The expressions are evaluated at compile time. - this is the main reason for using constexpr. When a value is computed at compile time, it helps your program to run faster and use less memory.


#include <iostream>

using namespace std;

// Pass by value
constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

// Pass by reference
constexpr float exp2(const float& x, const int& n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp2(x * x, n / 2) :
        exp2(x * x, (n - 1) / 2) * x;
}

// Compile-time computation of array length
template<typename T, int N>
constexpr int length(const T(&)[N])
{
    return N;
}

// Recursive constexpr function
constexpr int fac(int n)
{
    return n == 1 ? 1 : n * fac(n - 1);
}

// User-defined type
class Foo
{
public:
    constexpr explicit Foo(int i) : _i(i) {}
    constexpr int GetValue() const
    {
        return _i;
    }
private:
    int _i;
};

int main()
{
    // foo is const:
    constexpr Foo foo(5);

    // Compile time:
    constexpr float x = exp(5, 3);
    constexpr float y { exp(2, 5) };
    constexpr int val = foo.GetValue();
    constexpr int f5 = fac(5);
    const int nums[] { 1, 2, 3, 4 };
    const int nums2[length(nums) * 2] { 1, 2, 3, 4, 5, 6, 7, 8 };

    // Run time:
    cout << "The value of foo is " << foo.GetValue() << endl;
}

Output
The value of foo is 5

Click here to debug 

The difference between const and constexpr 

1. Initialisation of const can deferred until run time whereas the constexpr must be initialised at compile time. 


Why constexpr is static?

constexpr in class context refer to instance or class member or function, so compiler can't determine if it's static or not, ie bound or not to specific instance. It's same reasoning as const specifier. 


Reference

microsoft

why constexpr is not non-static?


Comments