constexpr是C++11的内容,其功能是 ==使指定的常量表达式获得在程序编译阶段计算出结果的能力,而不必等到程序运行阶段== 。

提出它的目的主要是为了解决const双重语义的问题。

"双重语义"是指”常量“与”只读“。

要搞清楚const与constexpr的关系,首先应该从”常量“与”只读“的区别入手。

只读 :侧重对变量或对象本身的属性或者权力。即某个变量没有权利(通过自身)更改其内存。如下:

1
2
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//第二种

第一种情况虽然在语义上和第二种相同,但编译器对二者的处理是不一样的。前者是 运行期常量 ,后者是 编译期常量 。可参考此链接

运行期常量不会进行常量折叠,编译器常量才会进行。为什么?因为运行期常量需要等到代码运行时才能确定,我们完全可以写出如下代码:

1
2
3
int k;
cin>>k;
const int a = max(k,10);

很显然,a的值需要等到我们输入k后才能确定。

此时,a就不可进行常量折叠了(因为常量折叠是在编译期进行的,而这时我们还没输入k),而是通过C语言的方式,在内存的常量区域开辟空间存放常量a的值,且此后获取a的值都需要访问其内存,而不是直接从符号表中获取。下面用一个最简单的例子进行对比:

1
2
const int size = 10;
int arr[size] = {0}; //没有问题
1
2
3
int a = 10;
const int size = a;
int arr[size] = {0}; //报错,因为完全可用在第一行与第二行直接再对a进行运行时修改。

最后再简单总结constconstexpr#define的区别:

  • const:

    1.const强调为"只读",本质仍是变量,可以被间接修改(通过引用方式)。

    2.const有可能为编译期常量,也可能为运行期常量。

    3.定义时会在内存中分配,后面调用只是给出对应的内存地址,不再分配,节省内存空间。

  • constexpr:

    1. constexpr强调"不可修改",是真正的常量。

    2. constexpr限定在了编译期常量 。

  • 宏定义:

    1.在预处理阶段展开;

    2.没有类型,不做类型检查,仅仅是展开;

    3.define宏定义时不分配内存,但是每次调用均会分配一次内存,会造成内存浪费;


有错误烦请读者指出。