C++11新标准:constexpr关键字
一、constexpr意义
将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式(不会改变,在编译过程中就能得到计算结果的表达式)。是一种比const更强的约束,这样可以得到更好的效率和安全性。
二、constexpr用法
1.修饰函数
/*1.如果size在编译时能确定,那么返回值就可以是constexpr,编译通过*/ constexpr int getSizeA(int size) { return 4*size; } /*2.编译通过,有告警:在constexpr中定义变量*/ constexpr int getSizeB(int size) { int index = 0; return 4; } /*3.编译通过,有告警:在constexpr中定义变量(这个有点迷糊)*/ constexpr int getSizeC(int size) { constexpr int index = 0; return 4; } /*4.编译通过,有告警:使用了if语句(使用switch也会告警)*/ constexpr int getSizeD(int size) { if(0) {} return 4; } /*5.定义变量并且没有初始化,编译不过*/ constexpr int getSizeE(int size) { int index; return 4; } /*6.rand()为运行期函数,不能在编译期确定,编译不过*/ constexpr int getSizeF(int size) { return 4*rand(); } /*7.使用了for,编译不过*/ constexpr int getSizeG(int size) { for(;0;) {} return 4*rand(); }
总结:constexpr修饰的函数,不能依赖任何运行期的信息,不要定义任何变量常量,并且必须尽量简单,要不就会编译不过或告警(坑)。
2.修饰类型
int tempA; cin>>tempA; const int ctempA = 4; const int ctempB = tempA; /*1.可以再编译器确定,编译通过*/ constexpr int conexprA = 4; constexpr int conexprB = conexprA + 1; constexpr int conexprC = getSizeA(conexprA); constexpr int conexprD = ctempA; /*2.不能在编译期决定,编译不过*/ constexpr int conexprE = tempA; constexpr int conexprF = ctempB;
总结:constexpr修饰的常量必须在编译期确定值,上面的例子也体现出了和const之间的差别。const既可以在编译期确定如ctempA,也可以在运行期确定如ctempB,使用范围更广。还有一点constexpr只能修饰字面值类型如算数类型、引用类型、指针以及后面介绍的字面值常量类。
3.修饰指针
int g_tempA = 4; const int g_conTempA = 4; constexpr int g_conexprTempA = 4; int main(void) { int tempA = 4; const int conTempA = 4; constexpr int conexprTempA = 4; /*1.正常运行,编译通过*/ const int *conptrA = &tempA; const int *conptrB = &conTempA; const int *conptrC = &conexprTempA; /*2.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/ constexpr int *conexprPtrA = &tempA; constexpr int *conexprPtrB = &conTempA constexpr int *conexprPtrC = &conexprTempA; /*3.第一个通过,后面两个不过,因为constexpr int *所限定的是指针是常量,故不能将常量的地址赋给顶层const*/ constexpr int *conexprPtrD = &g_tempA; constexpr int *conexprPtrE = &g_conTempA constexpr int *conexprPtrF = &g_conexprTempA; /*4.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/ constexpr const int *conexprConPtrA = &tempA; constexpr const int *conexprConPtrB = &conTempA; constexpr const int *conexprConPtrC = &conexprTempA; /*5.正常运行,编译通过*/ constexpr const int *conexprConPtrD = &g_tempA; constexpr const int *conexprConPtrE = &g_conTempA; constexpr const int *conexprConPtrF = &g_conexprTempA; return 0; }
总结:constexpr指针不能用局部变量赋值,const指针可以;constexpr指针里是顶层const,即指针是常量,而不是所指向的类型是常量,如果要指向的类型也为常量,要用constexpr const来修饰。
4.修饰引用
int g_tempA = 4; const int g_conTempA = 4; constexpr int g_conexprTempA = 4; int main(void) { int tempA = 4; const int conTempA = 4; constexpr int conexprTempA = 4; /*1.正常运行,编译通过*/ const int &conptrA = tempA; const int &conptrB = conTempA; const int &conptrC = conexprTempA; /*2.有两个问题:一是引用到局部变量,不能再编译器确定;二是conexprPtrB和conexprPtrC应该为constexpr const类型,编译不过*/ constexpr int &conexprPtrA = tempA; constexpr int &conexprPtrB = conTempA constexpr int &conexprPtrC = conexprTempA; /*3.第一个编译通过,后两个不通过,原因是因为conexprPtrE和conexprPtrF应该为constexpr const类型*/ constexpr int &conexprPtrD = g_tempA; constexpr int &conexprPtrE = g_conTempA; constexpr int &conexprPtrF = g_conexprTempA; /*4.正常运行,编译通过*/ constexpr const int &conexprConPtrD = g_tempA; constexpr const int &conexprConPtrE = g_conTempA; constexpr const int &conexprConPtrF = g_conexprTempA; return 0; }
总结:简单的说constexpr所引用的对象必须在编译期就决定地址。还有一个奇葩的地方就是可以通过上例conexprPtrD来修改g_tempA的值,也就是说constexpr修饰的引用不是常量,如果要确保其实常量引用需要constexpr const来修饰。
5.修饰类(未完待续)