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