0%

C++ 中 const 与 constexpr 的区别

原文地址: const 和 constexpr 区别与联系

提到 const 和 constexpr, 就需要引入常量表达式。常量表达式是指值不会改变, 并且编译过程就能得到计算结果的表达式. => 编译阶段就能得到值, 并且不能改变.

const 修饰对象无法修改, constexpr 更侧重于修饰对象编译期确定且无法修改. 具体区别, 体现在以下两个方面:

修饰变量

const 变量,表示一个变量无法改变,但初值并不确定, 不能在编译阶段决定.

比如,

1
const int sz = get_size(); // 虽然sz无法改变, 但get_size()

编译阶段无法确定值,也就是说 sz 不是常量表达式

constexpr 变量,编译器在编译阶段验证变量是否为一个常量表达式.

constexpr 侧重变量初值编译阶段确定,且无法修改. 如果认定变量是一个常量表达式,就把它声明称 constexpr 类型.

1
2
3
constexpr int mf = 20; // 字面量20是常量表达式
constexpr int limit = mf + 1; // mf + 1是常量表达式
constexpr int sz = size(); // 只有当size是constexpr函数时, 才是正确的

修饰指针

const 修饰指针分为两种情况:顶层 const, 底层 const.

顶层 const 代表指针变量自身无法修改;底层 const 代表指针所指对象无法修改.

1
2
3
4
5
6
7
8
int i = 10;
int *const p1 = &i; // 顶层const
const int *p2 = &i; // 底层const

p1 = new int(20); // 错误, 顶层const指针自身无法修改
p2 = new int(30); // 正确, 底层const指针可以修改
*p1 = 40; // 正确, 顶层const指针指向的对象可以修改
*p2 = 40; // 错误, 底层const指针指向的对象无法修改

constexpr 修饰指针,仅对指针有效,与指针所指对象无关

1
2
3
4
5
6
7
8
9
// j的定义必须放在函数体外
int j = 30;

// 函数体内
constexpr int *pp1 = &j; // 等价于 int constexpr *pp1 = &j;
cout << *pp1 << endl; // 30
*pp1 = 40;
cout << j << endl; // 40
pp1 = nullptr; // 错误, constexpr指针无法修改

修饰函数

const 修饰成员函数,通常称为 const 函数, 表示该函数不会修改类的状态(即不会通过任何方式修改类数据成员). 另外, const 类对象,只能调用 const 函数,确保不会修改类的数据成员.

constexpr 无法修饰成员函数,只能作为函数返回值类型, 表明该函数返回的是一个编译期可确定的常量;constexpr 被隐式隐式指定为内联函数,只能在类的声明中定义(.h 文件).

参见 C++ 的 const 类成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// A.h
class A{
public:
A():curSize(10) {}
void setSize(int size) { size++; }

// const函数
int size() const { return curSize; } // 正确示例:不写任何数据成员
int size() const { curSize = 2; return curSize; }; // 错误示例: const函数不能修改任何类的数据成员
int size() const { setSize(); return curSize; } // 错误示例:const函数不能调用任何可能导致类的数据成员改变的函数, 也就是说, 如果调用自身成员函数, 只能调用const函数
const int size() { return curSize; } // 函数返回值为const类型:函数体可以修改数据成员, 但返回类型是const, 也就是调用者无法修改

// 函数返回值为constexpr类型
constexpr int getMaxSize() { return INT_MAX; } // 正确示例:返回常量值
// 错误示例:vec.size()运行时确定, 不能在编译期决定
constexpr int getMaxSize()
{
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
return vec.size();
}
// 正确:虽然看起来返回的是变量, 但编译器可确定
constexpr int getMaxSize(int a, int b)
{
return a + b;
}

private:
int curSize;
}

// A.cpp
// 错误示例:constexpr被隐式指定为内联函数, 只能在.h 类内定义, 不能在类实现文件(.cpp)中定义
constexpr int A::getMaxsize()
{
return 1;
}