const 与 constexpr
一、问题:
遇到过该问题:在类中,与static
关键字一起使用时, 未在类外进行再次声明,导致编译报错
class A
{public:void txMsg(const int& x) const;void rxMsg(const int x);void func();private:static const int m_data1 = 10; static constexpr int m_data2 = 20;static inline const int m_data3 = 30;
};// 正确做法, 类外进行再次声明
const int A::m_data1;
constexpr int A::m_data2; A::func()
{this->txMsg(m_data1); this->rxMsg(m_data1);this->txMsg(m_data2); this->rxMsg(m_data2);this->txMsg(m_data3); this->rxMsg(m_data3);
}//错误做法, 未进行再次声明A::func()
{this->txMsg(m_data1); // 编译报错this->rxMsg(m_data1);this->txMsg(m_data2); // 编译报错this->rxMsg(m_data2);this->txMsg(m_data3); this->rxMsg(m_data3);
}
原因就在于static
关键字上,单一定义原则(ODR),当静态成员变量需要取地址时,必须在类外进行再次声明,否则编译器会报错。
类型 | 类内初始化 | 是否需要类外再次声明 |
---|---|---|
static const int | 可以 | 如果要被取地址,需要 |
static const 非整型 | 不可以 | 需要 |
static constexpr 任意类型 | 可以 | 如果要被取地址,需要 |
static inline const 任意类型 | 可以 | 不需要 |
二、 const 与 constexpr的区别
回顾下内存区域:
内存区域 | 存储内容 | 生命周期 | 管理对象 |
---|---|---|---|
代码区 | 存放函数体的二进制代码 | 程序运行期间 | 操作系统 |
全局区 | 存放全局变量和静态变量以及常量 | 程序运行期间 | 操作系统 |
栈区 | 存放函数的参数值,局部变量 | 函数运行期间 | 编译器自动分配释放 |
堆区 | 存放new ,malloc 分配的内存数据 | 程序运行期间 | 程序员手动释放,否则由操作系统回收 |
-
const:可以修饰多种变量,包括局部变量、全局变量、静态变量等;因此const变量的内存区域取决于它定义的位置
- 局部变量:栈区
void func() {// 局部变量,栈区const int local_var = 10; }
- 局部变量:堆区
void func() {// 堆区const int* heap_var = new const int(10);delete heap_var; }
- 全局变量:全局区
const int global_var = 10; // 全局区
- 静态变量:全局区
class A
{private:static const int static_var = 10; // 全局区
};// 需要在类外初始化,否则编译器会报错,尤其是函数引用到该值时
const int A::static_var = 10; // 全局区
- constexpr: 是 编译期常量(还包含表达式),必须在编译期就能计算出来;但它可能会被编译期给优化成一个立即数(取决于编译器)
- 全局变量:可能只在编译期存在
constexpr int global_var = 10;
- 局部变量:可能被编译器优化成一个立即数
void func() {constexpr int local_expr = 100;int array[local_expr]; }
- 常见场景:
- 对数组进行初始化:
int main() {const int arr_size = 10;int arr[arr_size]; // 报错,因为arr_size在编译期无法确定// 如果想要const修饰的变量通过,那么得让它分配得内存,在编译期就能通过constexpr int arr_size_expr = 10;int arr[arr_size_expr]; // 正确return 0; }
switch
的case标签:
const int red = 1;switch(color){case red: // 报错,因为red在编译期无法确定// 即便加上 sattic关键字,也不行case 1: // 正确// ... }constexpr int red_expr = 1; switch(color){case red_expr: // 正确// ... }
- 替换宏定义:
#define squre(x) { return x * x; } //不推荐constexpr int squre_expr(int x) { return x * x; } // 推荐 float array[squre_expr(3)]; // 正确
三、小结:
constexpr
是C++11引入的,用于定义编译期常量。它不仅可以修饰变量,还可以修饰表达式,并且要求该表达式在编译时就能计算出来。相比之下,const关键字主要用于修饰变量,并且在运行时也可能存在(取决于变量的存储位置)。- 大部分场景推荐使用
constexpr
,因为它提供了更严格的约束和更好的性能。