// 1) int url[10];//正确 // 2) int url[6 + 4];//正确 // 3) int length = 6; int url[length];//错误,length是变量上述代码演示了 3 种定义 url 数组的方式,其中第 1、2 种定义 url 数组时,长度分别为 10 和 6+4,显然它们都是常量表达式,可以用于表示数组的长度;第 3 种 url 数组的长度为 length,它是变量而非常量,因此不是一个常量表达式,无法用于表示数组的长度。
我们知道,C++ 程序的执行过程大致要经历编译、链接、运行这 3 个阶段。值得一提的是,常量表达式和非常量表达式的计算时机不同,非常量表达式只能在程序运行阶段计算出结果;而常量表达式的计算往往发生在程序的编译阶段,这可以极大提高程序的执行效率,因为表达式只需要在编译阶段计算一次,节省了每次程序运行时都需要计算一次的时间。常量表达式的应用场景还有很多,比如匿名枚举、switch-case 结构中的 case 表达式等,感兴趣的读者可自行编码测试,这里不再过多举例。
注意,获得在编译阶段计算出结果的能力,并不代表 constexpr 修饰的表达式一定会在程序编译阶段被执行,具体的计算时机还是编译器说了算。
#include <iostream> using namespace std; int main() { constexpr int num = 1 + 2 + 3; int url[num] = {1,2,3,4,5,6}; couts<< url[1] << endl; return 0; }程序执行结果为:
2
可以看到,程序第 6 行使用 constexpr 修饰 num 变量,同时将 "1+2+3" 这个常量表达式赋值给 num。由此,编译器就可以在编译时期对 num 这个表达式进行计算,因为 num 可以作为定义数组时的长度。读者可尝试将 constexpr 删除,此时编译器会提示“url[num] 定义中 num 不可用作常量”。
另外需要重点提出的是,当常量表达式中包含浮点数时,考虑到程序编译和运行所在的系统环境可能不同,常量表达式在编译阶段和运行阶段计算出的结果精度很可能会受到影响,因此 C++11 标准规定,浮点常量表达式在编译阶段计算的精度要至少等于(或者高于)运行阶段计算出的精度。注意,const 和 constexpr 并不相同,关于它们的区别,我们会在下一节做详细讲解。
constexpr int display(int x) { int ret = 1 + 2 + x; return ret; }注意,这个函数是无法通过编译的,因为该函数的返回值用 constexpr 修饰,但函数内部包含多条语句。
constexpr int display(int x) { //可以添加 using 执行、typedef 语句以及 static_assert 断言 return 1 + 2 + x; }可以看到,display() 函数的返回值是用 constexpr 修饰的 int 类型值,且该函数的函数体中只包含一个 return 语句。
constexpr void display() { //函数体 }像上面这样定义的返回值类型为 void 的函数,不属于常量表达式函数。原因很简单,因为通过类似的函数根本无法获得一个常量。
#include <iostream> using namespace std; //普通函数的声明 int noconst_dis(int x); //常量表达式函数的声明 constexpr int display(int x); //常量表达式函数的定义 constexpr int display(int x){ return 1 + 2 + x; } int main() { //调用常量表达式函数 int a[display(3)] = { 1,2,3,4 }; cout << a[2] << endl; //调用普通函数 cout << noconst_dis(3) << endl; return 0; } //普通函数的定义 int noconst_dis(int x) { return 1 + 2 + x; }程序执行结果为:
3
6
可以看到,普通函数在调用时,只需要保证调用位置之前有相应的声明即可;而常量表达式函数则不同,调用位置之前必须要有该函数的定义,否则会导致程序编译失败。读者可自行将 display() 常量表达式函数的定义调整到 main() 函数之后,查看编译器的报错信息。
#include <iostream> using namespace std; int num = 3; constexpr int display(int x){ return num + x; } int main() { //调用常量表达式函数 int a[display(3)] = { 1,2,3,4 }; return 0; }该程序无法通过编译,编译器报“display(3) 的结果不是常量”的异常。
注意,在常量表达式函数的 return 语句中,不能包含赋值的操作(例如 return x=1 在常量表达式函数中不允许的)。另外,用 constexpr 修改函数时,函数本身也是支持递归的,感兴趣的读者可自行尝试编码测试。
#include <iostream> using namespace std; //自定义类型的定义 constexpr struct myType { const char* name; int age; //其它结构体成员 }; int main() { constexpr struct myType mt { "zhangsan", 10 }; cout << mt.name << " " << mt.age << endl; return 0; }此程序是无法通过编译的,编译器会抛出“constexpr不能修饰自定义类型”的异常。
#include <iostream> using namespace std; //自定义类型的定义 struct myType { constexpr myType(char *name,int age):name(name),age(age){}; const char* name; int age; //其它结构体成员 }; int main() { constexpr struct myType mt { "zhangsan", 10 }; cout << mt.name << " " << mt.age << endl; return 0; }程序执行结果为:
zhangsan 10
可以看到,在 myType 结构体中自定义有一个构造函数,借助此函数,用 constexpr 修饰的 myType 类型的 my 常量即可通过编译。#include <iostream> using namespace std; //自定义类型的定义 class myType { public: constexpr myType(const char *name,int age):name(name),age(age){}; constexpr const char * getname(){ return name; } constexpr int getage(){ return age; } private: const char* name; int age; //其它结构体成员 }; int main() { constexpr struct myType mt { "zhangsan", 10 }; constexpr const char * name = mt.getname(); constexpr int age = mt.getage(); cout << name << " " << age << endl; return 0; }程序执行结果为:
zhangsan 10
注意,C++11 标准中,不支持用 constexpr 修饰带有 virtual 的成员方法。
#include <iostream> using namespace std; //自定义类型的定义 struct myType { const char* name; int age; //其它结构体成员 }; //模板函数 template<typename T> constexpr T dispaly(T t){ return t; } int main() { struct myType stu{"zhangsan",10}; //普通函数 struct myType ret = dispaly(stu); cout << ret.name << " " << ret.age << endl; //常量表达式函数 constexpr int ret1 = dispaly(10); cout << ret1 << endl; return 0; }程序执行结果为:
zhangsan 10
10
Copyright © 广州京杭网络科技有限公司 2005-2025 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有