const 和 constexpr 的详解和区别

constconstexpr

constexpr

常量表达式:是指值不会改变且在编译期间就可以计算处结果的表达式

常量表达式是由

  • 数据类型
  • 数据初始值

决定的。

int a = 1 a 不是一个常量表达式,因为它的数据类型不是常量

const int b = get_val() b不是一个常量表达式,因为它的初始值不是常量

由于使用const 不能确定一个表达式是不是常量,因此C++11使用constexpr 修饰符,可以让编译器在编译期间检查一个表达式是不是常量。

下面这个程序是不正确的,因为a虽然被const 修饰,但它并不是常量表达式,它引用了非常量表达式,因此在编译期间就可以确定它不是常量表达式

1
2
3
4
5
6
7
8
9
int main(void)
{
int b = 3;
const int a = b;
constexpr int i = a;
int arr[i];

return 0;
}

而只要将a = b 改为 a = 1 程序就不会报错了,因为此时a的确是一个常量表达式

constexpr 函数

普通的函数不是常量表达式,但我们可以定义constexpr 类型的函数,它可以作为常量表达式,但这个函数必须简单到可以在编译期间被确定

constexpr 指针 & 引用

constexpr 指向的位置必须是全局变量,静态变量,或者字面值等放在.bss.data 位置的数据,而不能是放在栈上的本地变量,因为他们必须在运行时才能确定位置,而不能在编译时确定位置。

const

const用来修饰变量,根据const 出现的位置,可以分为两种const :顶层const 和底层const

顶层const 可以表示任意的对象是常量

1
2
const int a = 1;    // a不能改变,是顶层const
int *const p = &a; // p不能改变指向,是顶层const

低层const 与指针和引用有关,表示,不能通过这个途径修改他们指向或者引用的变量

1
2
const int *p = &a;    // 指向a的指针,不能通过这个指针修改a的值,是底层const
const int &ref = a; // a的引用,不能通过这种方式修改a, 是底层const

const 与参数传递

  • 在参数传递时,顶层const会被忽略掉,因此下面这两个函数不能重载,因为他们忽略掉顶层const 后没有区别

    1
    2
    void func(int i);
    void func(const int i);
  • 可以使用非常量初始化底层const ,但不能使用常量去初始化非底层const

    1
    2
    3
    4
    int a = 1;
    const int b = 1;
    const int &ref_1 = a; // 可以, 可以使用非常量初始化底层const
    int &ret_2 = b; // 不可以, 不能使用常量去初始化非底层const
  • 非常量的引用必须是左值,常量的引用可以是左值,也可以是右值

    1
    2
    const int &a = 1;  // 正确
    int &b = 2; // 错误

    因此尽量将不会改变形参的函数的形参定义成底层const ,因为这样定义我们可以使用字面量(rvalue)。