【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中加入的关键字,它可以使用在函数和变量上,可以让函数或者变量在编译期间直接算出结果

先看一段代码

cpp 复制代码
#include <stdio.h>
int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    int r = sqr(2);
    printf("%d",r);
    return 0;
}

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

看汇编代码

上述代码每一步都执行了,包括赋值、调用sqr

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

cpp 复制代码
#include <stdio.h>
constexpr int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    constexpr int r = sqr(2);
    printf("%d",r);
    return 0;
}

看汇编代码

可以看到,sqr并没有被调用,程序在运行前,就已经被编译程序直接计算出了2的平方为4

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

不过,这三个关键字,同样的,必须是程序在未运行前,就可以通过计算确定出的值

,只要输入一个已知的值,就一定可以计算出一个值,这样才能用

比如上例改为

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

会报错

xml 复制代码
 error: 'x' is not a constant expression

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

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

cpp 复制代码
#include <stdio.h>
int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    constexpr int r = sqr(2);
    printf("%d",r);
    return 0;
}

同样也会报错

xml 复制代码
 error: call to non-'constexpr' function 'int sqr(int)'

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

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

cpp 复制代码
#include <stdio.h>
constexpr int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    int r = sqr(2);//sqr(x)
    printf("%d",r);
    return 0;
}

生成的汇编,就像一个函数调用了一样

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

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

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

xml 复制代码
 the value of 'a' is not usable in a constant expression

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

修改他的方法,有两种

把int a = 6改为const

cpp 复制代码
const int a = 6

或者

cpp 复制代码
constexpr int a = 6;

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

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

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

报错如下

xml 复制代码
 error: assignment of read-only variable 'r'

consteval

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

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

cpp 复制代码
#include <stdio.h>
consteval int sqr(int n)
{
    return n * n;
}
int main(int x, char**) 
{   
    int r = sqr(x);
    printf("%d",r);
    return 0;
}

错误

xml 复制代码
'x' is not a constant expression

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

cpp 复制代码
int r = sqr(2);

才可以

constinit

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

cpp 复制代码
constexpr int sqr(int n)
{
    return n * n;
}
constinit int r = sqr(2);

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

以上代码可以顺利编译通过

但是如果,改为如下

cpp 复制代码
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;
}

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

xml 复制代码
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改为常量

cpp 复制代码
constexpr int g =1;
//const int g =1;

这两种都可以

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

cpp 复制代码
constinit int r = sqr(2);

移入main中会报错

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

错误如下

xml 复制代码
'constinit' can only be applied to a variable with static or thread storage duration

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

cpp 复制代码
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;
}

总结

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

相关推荐
Hello-Mr.Wang几秒前
vue3中开发引导页的方法
开发语言·前端·javascript
救救孩子把3 分钟前
Java基础之IO流
java·开发语言
WG_174 分钟前
C++多态
开发语言·c++·面试
宇卿.11 分钟前
Java键盘输入语句
java·开发语言
Amo Xiang21 分钟前
2024 Python3.10 系统入门+进阶(十五):文件及目录操作
开发语言·python
friklogff34 分钟前
【C#生态园】提升C#开发效率:深入了解自然语言处理库与工具
开发语言·c#·区块链
Charles Ray2 小时前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
重生之我在20年代敲代码2 小时前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记
爱上语文2 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql