constexpr
是在 C++11 中引入的关键字。它的作用就是实现在编译时对表达式求解,运行时直接使用结果,避免一些计算,从而提高程序的效率。该关键字可以用于声明常量、函数、对象、和模板参数。
1. constexpr 常量
常量是指在程序运行过程中其值不会发生变化的量,比如数学中的圆周率 π,物理中的光速,以及程序中需要多次使用的固定参数。
常量分为:
- 编译期常量
- 值在编译时确定:编译期常量的值必须在编译时就能确定,运行时不能修改。
- 不占运行时内存:编译器会将常量值直接插入到使用它的地方,因此在运行时不占用额外的内存。
- 优化:编译器可以对编译期常量进行优化,减少不必要的计算和存储。
- 运行期常量:
- 值在运行时确定:运行期常量的值只能在程序运行时确定,运行时不能修改。
- 占用运行时内存:因为编译器在编译时无法确定其值,所以需要在运行时为其分配内存。
- 优化:编译器一般无法对运行期常量进行优化。
在 C++ 中定义一个常量可以使用两个关键字:const 和 constexpr,两者的区别:
- const 可以定义编译期常量,也可以定义运行期常量
- constexpr 只能用于定义编译期的常量
#if 1 #include <iostream> using namespace std; // 1. const void test01() { // 编译期常量 const int a = 10; /* int ret1 = a + a; 00007FF625B21EE2 mov dword ptr [ret1],14h */ int ret1 = a + a; // 运行期常量 int temp = 20; const int b = temp; /* int ret2 = b + b; 00007FF76AA01EF6 mov eax,dword ptr [b] 00007FF76AA01EF9 mov ecx,dword ptr [b] 00007FF76AA01EFC add ecx,eax 00007FF76AA01EFE mov eax,ecx 00007FF76AA01F00 mov dword ptr [ret2],eax */ int ret2 = b + b; } // 2. constexpr void test02() { // 编译期常量 constexpr int a = 10; /* int ret = a + a; 00007FF7AEA71EE2 mov dword ptr [ret],14h */ int ret = a + a; int temp = 20; // 报错:表达式的计算结果不是常数 // constexpr int b = temp; } int main() { test01(); test02(); return 0; } #endif
2. constexpr 函数
普通函数需要在运行时执行以获得计算结果,而 constexpr 函数则允许在编译时执函数计算结果,这可以将一些计算由运行时转移到编译时,从而提高程序运行时的性能。
#if 1 #include <iostream> using namespace std; constexpr int calculate(int a, int b, char m) { // 条件语句 if (0 == a && 0 == b) { return 0; } // 局部变量 int temp1 = 0; int temp2 = 0; // 循环语句 for (int i = 0; i < a; ++i) { temp1 += i; } for (int i = 0; i < b; ++i) { temp2 += i; } // 条件语句 int result = 0; switch (m) { case '+': result = temp1 + temp2; break; case '-': result = temp1 - temp2; break; case '*': result = temp1 * temp2; break; case '/': result = temp1 / temp2; break; default: break; } return result; } void test() { constexpr int a = 10; constexpr int b = 20; constexpr char m = '+'; /* 注意点: 1. 函数的参数必须是编译期常量 2. 函数的返回值必须使用 constexpr 变量承接 */ // 1. 编译期计算 /* constexpr int ret1 = calculate(a, b, m); 00007FF628F918FD mov dword ptr [ret1],0EBh */ constexpr int ret1 = calculate(a, b, m); // 2. 运行期计算 /* int ret2 = calculate(a, b, m); 00007FF78B3D1904 mov r8b,2Bh 00007FF78B3D1907 mov edx,14h 00007FF78B3D190C mov ecx,0Ah 00007FF78B3D1911 call calculate (07FF78B3D13DEh) 00007FF78B3D1916 mov dword ptr [ret2],eax */ int ret2 = calculate(a, b, m); } int main() { test(); return 0; } #endif
3. constexpr 对象
constexpr
对象是使用 constexpr
关键字声明的对象,其值在编译时就已经确定,并且可以用于在编译时执行某些操作。与常量表达式相比,constexpr
对象更加灵活,因为它们不仅可以在编译时用于确定常量表达式的值,还可以在运行时用于一般的计算和操作。
#if 1 #include <iostream> using namespace std; class MyObject { public: // 1. 构造函数必须使用初始化列表 constexpr MyObject(int a, int b, int c) : m_a(a), m_b(b), m_c(c){} // 2. 常函数(编译器、运行期调用) constexpr int const_sum() const { return m_a + m_b + m_c; } // 3. 普通函数(运行时调用) int common_sum() { return m_a + m_b + m_c; } private: int m_a; int m_b; int m_c; }; void test() { // 常对象 constexpr MyObject o1(10, 20, 30); /* constexpr int r1 = o1.const_sum(); 00007FF6BC641AB2 mov dword ptr [ret1],3Ch */ constexpr int r1 = o1.const_sum(); cout << r1 << endl; // 不能修改的常对象 const MyObject o2(10, 10, 10); /* int r2 = o2.const_sum(); 00007FF7CCD02515 lea rcx,[o2] 00007FF7CCD02519 call MyObject::const_sum (07FF7CCD01271h) 00007FF7CCD0251E mov dword ptr [r2],eax */ int r2 = o2.const_sum(); cout << r2 << endl; // 普通对象 MyObject o3(10, 20, 30); /* int ret2 = obj2.const_sum(); 00007FF7A0491AF5 lea rcx,[obj2] 00007FF7A0491AF9 call MyObject::const_sum (07FF7A049121Ch) 00007FF7A0491AFE mov dword ptr [ret2],eax int ret3 = obj2.common_sum(); 00007FF7A0491B04 lea rcx,[obj2] 00007FF7A0491B08 call MyObject::common_sum (07FF7A0491181h) 00007FF7A0491B0D mov dword ptr [ret3],eax */ int r3 = o3.const_sum(); int r4 = o3.common_sum(); cout << r3 << " " << r4 << endl; } int main() { test(); return 0; } #endif
至此,关于 constexpt 讲解完毕,希望对你有所帮助!