浅谈 C++ 中的 const 和 constexpr
C++11中新增加了用于指示常量表达式的constexpr关键字。在这里区分两者的用法。
基本含义/语法
const和constexpr都可以来修饰对象和函数。
修饰对象的时候两者之间最基本的区别是:
- const修饰一个对象表示它是常量。这暗示对象一经初始化就不会再变动了,并且允许编译器使用这个特点优化程序。这也防止程序员修改了本不应该修改的对象。
- constexpr是修饰一个常量表达式。但请注意constexpr不是修饰常量表达式的唯一途径。
修饰函数的时候两者之间最基本的区别是:
- const只能用于非静态成员的函数而不是所有函数。它保证成员函数不修改任何非静态数据。
- constexpr可以用于含参和无参函数。constexpr函数适用于常量表达式,只有在下面的情况下编译器才会接受constexpr函数: 1.函数体必须足够简单,除了typedef和静态元素,只允许有return语句。如构造函数只能有初始化列表,typedef和静态元素 (实际上在C++14标准中已经允许定义语句存在于constexpr函数体内了) 2.参数和返回值必须是字面值类型
常量表达式
常量表达式的概念如下
- 必须是可以在编译阶段被识别的。比如模版的参数/数组的大小。
-
template <int N> class fixed_size_list { /*...*/ }; fixed_size_list<X> mylist; // <-- X必须是字面值类型 int numbers[X]; // <-- X必须是字面值类型
但是注意下面两点
- 用constexpr修饰某物并不保证它一定在编译时被计算,也可以在运行时被计算。
- 不用constexpr也可能是一个常量表达式,如
int main() { const int N = 3; int num[N] = {1,2,3,};//N是一个常量表达式} //N在声明时初始化,满足常量表达式的标准,虽然没用constexpr
所以到底什么时候用constexpr?
- 像上面的N,虽然没有用 constexpr 仍然是一个常量表达式。事实上,const本身和它的枚举类型若在声明时初始化那么就是一个常量表达式。
- 在函数中若有常量表达式那么必须用constexpr,仅仅满足常量表达式的条件是不够的,如
template <int N> class list { }; constexpr int sqr1(int arg) { return arg*arg; } int sqr2(int arg) { return arg*arg; } int main() { const int X = 2; list<sqr1(X)> mylist1;//可以,因为sqr1时constexpr函数 list<sqr2(X)> mylist2;//不可以,因为sqr2不是constexpr函数 return 0;}
什么时候该同时使用两者?
- 在修饰变量的时候
修饰变量时没有必要同时使用const和constexpr 因为constexpr包含了 const的含义
constexpr const int N = 5;
constexpr int N = 5;
//两行的意思完全相同
然而注意有一些情况const和constexpr在修饰不同的东西,比如
static constexpr int N = 3;
int main()
{
constexpr const int *NP = &N;
return 0;
}
在这里constexpr和const都必须要有。constexpr表示NP指针本身是常量表达式,而const表示指向的值是一个常量。去掉const之后无法编译,因为不能用正常指针指向常量。
- 在修饰成员函数的时候
在C++11中,对成员函数而言constexpr同样包含const的含义。然而在C++14中可能已经改变了。如
constexpr void f();
以后可能必须写成
constexpr void f() const;
虽然目前写成const仍然有效,但最好使用constexpr来防止以后修改大量代码的可能性。
from:https://zhuanlan.zhihu.com/p/20206577/