【C++】用constexpr,constinit,consteval让程序跑的快一点

从C++11加入constexpr关键字开始,到C++20又加入了consteval ,constinit ,有3个const打头的关键字

虽然是以const开头的,不过这3个关键字主要是指示在编译时候的动作,它们都是在编译时就已经被编译程序处理,并非在运行时被机器处理

下面逐一介绍

以下代码在cygwin gcc 11.4 cmake 3.25中调试通过

constexpr

constexpr是在C++11中加入的关键字,它可以使用在函数和变量上,可以让函数或者变量在编译期间直接算出结果
先看一段代码

#include 
int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    int r = sqr(2);
    printf("%d",r);
    return 0;
}

以上代码,调用sqr 计算2的平方

看汇编代码
【C++】用constexpr,constinit,consteval让程序跑的快一点_第1张图片
上述代码每一步都执行了,包括赋值、调用sqr

然后在对sqr前和变量r前加上constexpr,再看使用constexpr修饰后的结果

#include 
constexpr int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    constexpr int r = sqr(2);
    printf("%d",r);
    return 0;
}

看汇编代码
【C++】用constexpr,constinit,consteval让程序跑的快一点_第2张图片
可以看到,sqr并没有被调用,程序在运行前,就已经被编译程序直接计算出了2的平方为4

这就是constexpr的作用,包括constinit,consteval也是这个作用。

不过,这三个关键字,同样的,必须是程序在未运行前,就可以通过计算确定出的值
,只要输入一个已知的值,就一定可以计算出一个值,这样才能用

比如上例改为

constexpr int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    constexpr int r = sqr(x);
    printf("%d",r);
    return 0;
}

会报错

 error: ‘x’ is not a constant expression

应为r 被修饰为constexpr ,所以在编译是必须已经可以产生确定的值才行,而sqr传入x是没有办法算出确定的值的,因为X也不确定

同理,代码如下,将sqr的constexpr 去掉

#include 
int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    constexpr int r = sqr(2);
    printf("%d",r);
    return 0;
}

同样也会报错

 error: call to non-‘constexpr’ function ‘int sqr(int)’

因为r 是需要一个可以确认的值,但sqr并不是一个可以,立即执行的确认函数

但是反过来没有问题,把变量r前的constexpr 去掉 ,直接传入数值,或者传入一个变量都可以了

#include 
constexpr int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    int r = sqr(2);//sqr(x)
    printf("%d",r);
    return 0;
}

生成的汇编,就像一个函数调用了一样
【C++】用constexpr,constinit,consteval让程序跑的快一点_第3张图片

下面,介绍一下constexpr 修饰变量的操作,有如下代码

int main(int x, char**) 
{   
    int a = 6;
    constexpr int r = a==6?1:2 ;
    printf("%d",r);
    return 0;
}

上述代码应为a不是常量,所以会报错

 the value of ‘a’ is not usable in a constant expression

原因也是应为a的不确定性导致

修改他的方法,有两种
把int a = 6改为const

const int a = 6

或者

constexpr int a = 6;

a变成不可修改的常量,r的值就可以被确定

其次,被constexpr 修饰的变量,是具有const属性的,不能被修改

int main(int x, char**) 
{   
    const int a = 6;
    constexpr int r = a==6?1:2 ;
    r = 3;
    printf("%d",r);
    return 0;
}

报错如下

 error: assignment of read-only variable ‘r’

consteval

constexpr可以使用在函数和变量上,consteval只能使用在函数上,强制为可以计算出结果的函数

上面介绍constexpr 时,如果变量不用constexpr修饰那么调用sqr时,可以传入变量,但是被consteval修饰的函数,就会报错了

#include 
consteval int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    int r = sqr(x);
    printf("%d",r);
    return 0;
}

错误

‘x’ is not a constant expression

这说明被consteval修饰的函数,必须一定要能即时计算才行,所以,要改为

int r = sqr(2);

才可以

constinit

和consteval一样,它也是强制在编译时产生结果的,只不过,consteval强制的是函数,constinit强制的变量

constexpr int sqr(int n)
{
    return n * n;
}
constinit int r = sqr(2);

int main(int x, char**) 
{   
    printf("%d",r);
    return 0;
}

以上代码可以顺利编译通过
但是如果,改为如下

int g =1;
constexpr int sqr(int n)
{
    return n * n;
}
constinit int r = sqr(g);

int main(int x, char**) 
{   
    printf("%d",r);
    return 0;
}

编译器将会产生相关两条错误

1.error: the value of 'g' is not usable in a constant expression
2.error: 'constinit' variable 'r' does not have a constant initializer

应为sqr传入了不是常量的数据,导致sqr不能判断出最终r是什么值
如果想让上例通过,需要将g改为常量

constexpr int g =1;
//const int g =1;

这两种都可以

因为constinit修饰的是静态或线程存储期的变量,所以,它不能在局部函数中出现,上例中,如果将

constinit int r = sqr(2);

移入main中会报错

int main(int x, char**) 
{   
    constinit int r = sqr(2);
    printf("%d",r);
    return 0;
}

错误如下

‘constinit’ can only be applied to a variable with static or thread storage duration

和constexpr修饰的变量不一样,constinit修饰的变量只做强制运算,并不会产生const 变量,所以constinit修饰的变量是可以修改的

constexpr int sqr(int n)
{
    return n * n;
}
constinit int r = sqr(2);
int main(int x, char**) 
{   
    r = 9; //可以
    printf("%d",r);
    return 0;
}

总结

从几个例子其实也可一看出来,这三个关键字的作用,都是为了让可以确定的运算结果,在编译时就首页被执行完毕,这样,运行时等于只是操作一个具体的常量,不会在执行多余的运算,也就提高了一点运行速度。也就是说,被这三个关键字修饰的变量或者函数,即使不使用编译器、程序不运行,也能准确的知道他最终的值.这时候才能使用上述三个关键字功能

你可能感兴趣的:(C++,c++,开发语言)