Introduction to constexpr and the difference between const and Const

Posted by starmikky on Wed, 26 Jan 2022 16:24:32 +0100

1. Constant expression

The so-called constant expression refers to an expression composed of multiple (≥ 1) constants. In other words, if the members in the expression are constants, the expression is a constant expression. That is, once the constant expression is determined, its value is not allowed to be changed.

The execution process of C + + program is roughly divided into three stages: compiling, linking and running. The execution stage of constant expression is different from that of non constant expression.

  • The non constant expression can only calculate the result in the running stage of the program;
  • The calculation of constant expression often occurs in the compilation stage of the program.

Therefore, the constant expression can greatly improve the execution efficiency of the program, because the constant expression only needs to be calculated once in the compilation stage, which saves the time that needs to be calculated once every time the program runs. The constexpr keyword is provided in c++11 to specify constant expressions. In the c++11 standard, constexpr can be used to modify ordinary variables, functions (including template functions) and class constructors.

2. constexpr

2.1 constexpr modifier constant

In the C++11 standard, constexpr can be used to modify constants or constant expressions, so that the variable can obtain the ability to calculate the results in the compilation stage.

#include <iostream>
using namespace std;
int main()
{
    constexpr int num = 1 + 2 + 3;
    int a[num] = {1,2,3,4,5,6};
    cout<< a[1] << endl;//2
    return 0;
}

2.2 constexpr modifier function

constexpr can also be used to modify the return value of a function, which is also called a constant expression function. If a function wants to become a constant expression function, it must meet the following four conditions at the same time:

Condition 1: the function body of the whole function can contain using instruction, typedef statement and static_ Only one return statement can be included in addition to the assert assertion.

  • Error example

The following code contains multiple statements and cannot be compiled.

constexpr int display(int x) {
    int ret = 1 + 2 + x;
    return ret;
}
  • Correct example
constexpr int display(int x) {
    //You can add using execution, typedef statements, and static_assert assertion
    return 1 + 2 + x;
}

Condition 2: the function must have a return value, that is, the return value type of the function cannot be void.

  • Error example
constexpr void display() {
    //Function body
}

Condition 3: you must define the function before using it.

As we know, the use of functions is divided into "Declaration" and "definition". Ordinary function calls only need to write the declaration part of the function in advance (the definition part of the function can be placed after the call position or even in other files), but the constant expression function must have the definition of the function before use.

  • Correct example
#include <iostream>
using namespace std;
//Declaration of ordinary functions
int noconst_dis(int x);
//Declaration of constant expression function
constexpr int display(int x);
//Definition of constant expression function
constexpr int display(int x){
    return 1 + 2 + x;
}
int main()
{
    //Call constant expression function
    int a[display(3)] = { 1,2,3,4 };
    cout << a[2] << endl;
    //Call normal function
    cout << noconst_dis(3) << endl;
    return 0;
}
//Definition of ordinary function
int noconst_dis(int x) {
    return 1 + 2 + x;
}

Condition 4: the expression returned by return must be a constant expression

  • Error example
#include <iostream>
using namespace std;
int num = 3;
constexpr int display(int x){
    return num + x;//Error, num non constant
}
int main()
{
    //Call constant expression function
    int a[display(3)] = { 1,2,3,4 };
    return 0;
}

2.3 constructor of constexpr modified class

For the data of C + + built-in type, you can directly modify it with constexpr, but if it is a custom data type (implemented with struct or class), you can't directly modify it with constexpr, but you can modify its constructor with constexpr.

  • Error example

This program cannot be compiled. The compiler will throw an exception that "constexpr cannot modify custom types".

#include <iostream>
using namespace std;
//Definition of custom type
constexpr struct myType {
    const char* name;
    int age;
    //Other structure members
};
int main()
{
    constexpr struct myType mt { "zhangsan", 10 };
    cout << mt.name << " " << mt.age << endl;
    return 0;
}
  • Correct example
#include <iostream>
using namespace std;
//Definition of custom type
struct myType {
    constexpr myType(char *name,int age):name(name),age(age){};
    const char* name;
    int age;
    //Other structure members
};
int main()
{
    constexpr struct myType mt { "zhangsan", 10 };
    cout << mt.name << " " << mt.age << endl;
    return 0;
}

2.4 constexpr modification template function

In C++11 syntax, constexpr can modify template functions. C++11 standard stipulates that if the instantiation result of the template function modified by constexpr does not meet the requirements of the constant expression function, constexpr will be automatically ignored, that is, the function is equivalent to an ordinary function.

#include <iostream>
using namespace std;
//Definition of custom type
struct myType {
    const char* name;
    int age;
    //Other structure members
};
//template function
template<typename T>
constexpr T dispaly(T t){
    return t;
}
int main()
{
    struct myType stu{"zhangsan",10};
    //Ordinary function
    struct myType ret = dispaly(stu);
    cout << ret.name << " " << ret.age << endl;
    //Constant expression function
    constexpr int ret1 = dispaly(10);
    cout << ret1 << endl;
    return 0;
}

3. The difference between constexpr and const

const represents a read-only variable. In actual use, it has two semantics.

  • Semantic one: func_ "const int x" in 1 emphasizes that x is a read-only variable, which is still a variable in nature and cannot be used to initialize the array container.
  • Semantic 2: func_ "const int x" in 2 indicates that while x is a read-only variable, X is also a constant with a value of 5, so it can be used to initialize the array container.
#include <iostream>
#include <array>
using namespace std;
void func_1(const int x){
    array <int,x> myarr{1,2,3,4,5};//Error, x is a read-only variable whose value needs to be determined at run time
    cout << myarr[1] << endl;
}
void func_2(){
    const int x = 5;
    array <int,x> myarr{1,2,3,4,5};//Correct, it has been initialized, and the value of x has been determined in the compilation stage
    cout << myarr[1] << endl;
}
int main()
{
   func_1(5);
   func_2();
}

In the C++11 standard, in order to solve the dual semantic problem of const keyword, the semantics of const representing "read-only" is retained, and the semantics of "constant" is divided into the newly added constexpr keyword. Therefore, in the C++11 standard, it is suggested to distinguish const from constexpr, that is, const is used for all scenes expressing "read-only" semantics and constexpr is used for scenes expressing "constant" semantics.

Some readers may ask, "read only" doesn't mean it can't be modified? The answer is No. there is no necessary connection between "read only" and "not allowed to be modified". For example:

#include <iostream>
using namespace std;
int main()
{
    int a = 10;
    const int & con_b = a;
    cout << con_b << endl;
    a = 20;
    cout << con_b << endl;
}

Output:

10
20

You can see that con is decorated with const in the program_ B variable, indicating that the variable is "read-only", that is, you cannot modify your own value through the variable itself. But that doesn't mean con_ The value of B cannot be changed indirectly with the help of other variables. By changing the value of a, con can be changed_ The value of B changes.

In most practical scenarios, const and constexpr can be mixed, for example:

const int a = 5 + 4;
constexpr int a = 5 + 4;

They are completely equivalent and can be calculated at the compilation stage of the program. However, in some scenarios, constexpr must be explicitly used, for example:

#include <iostream>
#include <array>
using namespace std;
constexpr int sqr1(int arg){
    return arg*arg;
}
const int sqr2(int arg){
    return arg*arg;
}
int main()
{
    array<int,sqr1(10)> mylist1;//Yes, because when sqr1, the constexpr function
    array<int,sqr2(10)> mylist1;//No, because sqr2 is not a constexpr function
    return 0;
}

reference resources:
http://c.biancheng.net/view/7781.html
http://c.biancheng.net/view/7807.html

Topics: C++ const