constexpr是C++11的内容,提出它的目的主要是为了解决const双重语义的问题。
"双重语义"是指”常量“与”只读“。
要搞清楚const与constexpr的关系,首先应该从”常量“与”只读“的区别入手。
”只读“:侧重对变量或对象本身的属性或者权力。即某个变量没有权利(通过自身)更改其内存。如下:
int b = 10;
const int a = b;
上面这条语句告诉我们,不可通过a变量来修改a中的内容,即不能完成如下
值得一提的是,这只是强调不可通过a来修改其内存,即不能继续以下操作:
a = 20;//a为只读,不可修改
但是却可以通过其他办法来改变其内存,很简单,使用指针修改即可:
int* pa = (int*)&a;
*pa = 30;//a的值就被修改成了30.
需要强调的是,C++中通过引用或指针修改const的值,只适用于赋的值为左值(变量)的情况!(即const int a = b;) 当被赋的值为右值(即字面量)时(const int a = 10;)便不可被修改
至于为什么,请看下文。
”常量“:侧重内存本身是常量,任何方式都无法修改此部分内存,如下:
constexpr int a = 10;
//a此时就与一个常量绑定了,在编译期间,a就会被10替换(类似于宏替换),以达到优化
注意:使用constexpr时,= 右边必须是右值(即字面常量),而不可为变量!
为了区分这两者,C++将“常量”交给了constexpr,将“只读”交给了const
还需要注意的是,C++与C不同,后者访问const 变量的值是通过其内存获取的,而前者是通过常量折叠(指const变量(即常量)值放在编译器的符号表中,计算时编译器直接从表中取值,省去了访问内存的时间,从而达到了优化)来获取其值的。所以当如下定义时,const与constexpr效果完全相同:
const int a = 10;//与下等价:
constexpr int a = 10;//这两种情况,a的值无论如何也无法改变
值得一提的是,下面两种情况是不等价的:
int b = 10;
const int a = b;//第一种
const int a = 10//第二种
第一种情况虽然在语义上和第二种相同,但编译器对二者的处理是不一样的。前者是运行期常量,后者是编译期常量。可参考此链接
运行期常量不会进行常量折叠,编译器常量才会进行。为什么?因为运行期常量需要等到代码运行时才能确定,我们完全可以写出如下代码:
很显然,a的值需要等到我们输入k后才能确定。
此时,a就不可进行常量折叠了(因为常量折叠是在编译期进行的,而这时我们还没输入k),而是通过C语言的方式,在内存的常量区域开辟空间存放常量a的值,且此后获取a的值都需要访问其内存,而不是直接从符号表中获取。下面用一个最简单的例子进行对比:
//没有问题
//报错。
需要补充说明的是,C++中的volatile关键字可以修饰const变量,使第二种情况下定义的const变量不从符号表中读取,则此时与第一种情况相同
(volatile具体如何使用不太清楚,此例仅供参考)
constexpr还有其他诸多特性,如常量表达式函数等,本文只浅谈constexpr与const的关系,其他内容就不详细展开了。
希望本文能够帮助到大家。