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 讲解完毕,希望对你有所帮助!


冀公网安备13050302001966号