C++ constexpr

本文详细解释了常量表达式、constexpr变量和函数在C++中的概念,以及它们在编译过程中的处理方式,包括区分常量和常量表达式的方法,constexpr变量的使用限制,以及constexpr函数的特性与应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、常量表达式

常量表达式:指值不会改变并且在编译过程就能得到计算结果的表达式;字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。

const int i = 20;  //  是常量表达式
int j = 10;        //  数据类型是普通的int而非const ,不是常量表达式 

int size(int i) {return i+1;}
const int k = size(10);  //  k 本身是一常量,但它的具体值需要调用size函数才能获取到,所以不是常量表达式

如何区分常量和常量表达式?
查看汇编代码,在编译过程中是否得到计算结果

int calculate(int i) 
{
    return i+1;
}

constexpr int calculate_1(int i)   // constexpr 后续讲解,这里只做比较
{
    return i+1;
}
int main(int argc, const char** argv) {
    const int k = calculate(10);
    const int k1 = calculate_1(20);
    return 0;
}

汇编代码部分:
	call	___main
	// 计算k的值
	movl	$10, (%esp)
	call	__Z9calculatei
	movl	%eax, 28(%esp)

	k的值需要调用calculate计算得来
	
	// 计算k1的值
	movl	$21, 24(%esp)
	k1值有编译器计算直接赋值

大家可以通过汇编代码,很容易理解 在编译过程中是否得到计算结果这句话

二、constexpr 变量

将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化;

constexpr int i = 20; // 20是常量表达式 正确
constexpr int j = i + 1; // i + 1是常量表达式

int j1 = 10;
constexpr int j2 = j1;  // 错误 j1不是常量表达式

constexpr int k1 = calculate(10);   // 错误
constexpr int k1 = calculate_1(10); // 正确
不能使用普通函数作为constexpr变量的初始值,可以用constexpr函数初始化constexpr变量;

一般情况下,当你认为变量是一个常量表达式时,可以用constexpr声明,提升代码的效率;

constexpr int i = 10;
int i1 = i + 1;   // 由于i是常量表达式 => int i1 = 10 + 1; 
									   => int i1 = 11;

int j = 20;
int j1 = j + 1;

movl	$10, 12(%esp)
movl	$11, 8(%esp)  // i1 = 11; 

movl	$20, 4(%esp)
movl	4(%esp), %eax
addl	$1, %eax     // j + 1 
movl	%eax, (%esp)  // j1 = j + 1

显然执行代码一有更高的效率;

三、字面值类型

可以用constexpr 声明的类型是字面值类型。算术类型、引用和指针都属于字面值类型,而自定义类、IO库、string类型不属于字面值类型,也不能被定义成constexpr;
constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址的对象。
函数体内定义的局部变量(非静态局部变量)存放在非固定的地址上,所以constexpr的指针不能指向此类变量。定义于所有函数体之外的对象其地址固定不变,可以用来初始化constexpr的地址。允许constexpr引用绑定到静态局部变量,constexpr指针能指向这样的变量。

int  j = 0;
int main(int argc, const char** argv) {
    constexpr int *j1 = &j;  // 成立  
    constexpr int &j2 = j;  // 成立  

	static int i = 0;
    constexpr int *i1 = &i;  // 成立
    constexpr int &i2 = i;  // 成立
    return 0;
}
constexpr 仅对指针有效,于指针所指的对象无关
const int *p = NULL; //p是一个指向常量的指针
constexpr int *p = nullptr; // p是一个指向整型的常量指针; constexpr 把它所定义的对象置为了顶层const

int i = 0 ;
constexpr int j = 0;   // i 和 j 都定义在函数体之外,全局变量

constexpr int *p1 = &j;   // p1 常量指针 指向整数j
constexpr const int *p2 = &i;  // p2 是常量指针 指向常量整数i

四、constexpr函数

constexpr 函数是指能用于常量表达式的函数。函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句;执行初始化任务时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr函数隐式地被转换为内联函数。传递的形参时常量表达式,则它的返回值也是常量表达式;

constexpr int calculate_1(int i) 
{
	i++;
    return i+1;
}
int main(int argc, const char** argv) {
	constexpr int k1 = calculate_1(1);  // 等价于 cont int k1 = 3;
	
	int i = 1;
	constexpr int k1 = calculate_1(i); // error 
}

constexpr函数和内联函数可以在程序中多次定义而不报错。因为编译器需要展开函数,所以光有函数声明是不够的,还需要函数的定义。但是对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致。 一般将内联函数与constexpr函数定义在头文件中。

// 标准写法
// b.h
constexpr int calculate_1(int i) 
{
	i++;
    return i+1;
}

// a.cpp
#include "b.h"
int main(int argc, const char** argv) {
	constexpr int k1 = calculate_1(1);  // 等价于 cont int k1 = 3;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值