• 【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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    看汇编代码
    在这里插入图片描述
    上述代码每一步都执行了,包括赋值、调用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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    看汇编代码
    在这里插入图片描述
    可以看到,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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    会报错

     error: ‘x’ is not a constant expression
    
    • 1

    应为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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    同样也会报错

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

    因为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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    生成的汇编,就像一个函数调用了一样
    在这里插入图片描述

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

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

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

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

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

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

    const int a = 6
    
    • 1

    或者

    constexpr int a = 6;
    
    • 1

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    报错如下

     error: assignment of read-only variable ‘r’
    
    • 1

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    错误

    ‘x’ is not a constant expression
    
    • 1

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

    int r = sqr(2);
    
    • 1

    才可以

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

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

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

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

    这两种都可以

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

    constinit int r = sqr(2);
    
    • 1

    移入main中会报错

    int main(int x, char**) 
    {   
        constinit int r = sqr(2);
        printf("%d",r);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    错误如下

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

    和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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    总结

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

  • 相关阅读:
    Android Qcom Display学习(零)
    漏洞修复:在应用程序中发现不必要的 Http 响应头
    【JY】YJK前处理参数详解及常见问题分析:控制信息(二)
    Python部分异常日志缺失
    【黑马程序员】MySQL学习记录二(SQL)案例
    善于利用GPT确实可以解决许多难题
    37.解数独(内含回溯算法重要思路)
    面试系列 - 正则表达式详解
    STM32的寄存器深度解析
    HTTP的演变
  • 原文地址:https://blog.csdn.net/weixin_44305576/article/details/133864286