tags:
- Cpp
Const in C++
现代C++中,我们会用const
来来做以下的三件事情:
对于第一种,constexpr
通常是更好的选择。对于 const
而言,最主要的作用就是用指针或引用传递参数、返回值时防止修改指令 (modifiable operand) 对参数和返回值的修改。防止修改指令对参数/返回值的修改,实际上就是将潜在的运行时 bugs 转换成了编译时的一些错误。而通常情况下,编译时错误很难被忽略,所以使用 const
可以让我们更好地创建接口 (interfaces) 。
而 constexpr
的职责是将运行时的一些计算转换成编译时计算。
在使用const
的时候,我们经常看到:
int const number = 123456;
char const msg[] = "hello";
上面的示例中,我们使用const
来定义一些不可修改的类型,第一个定义了一个不可修改的int
类型,下面定义了一个用const char
所组成的array。这些类型的共同点是可读但不可修改(写)。
这些不可修改的类型只有在初始化的时候给初值,并且C++要求const
类型的对象必须初始化。
namespace example{
int const un_initialized_; // Error: missing initializer.
extern int const initialized_; // Ok: this is a declaration, not a def
}
Constexpr is conster than const.
在 C/C++ 中,在 array 中对维度的定义也必须是一个常值,叫做 integer constant expression。
int x[n]; // Error
int y[10]; // OK
int const level = 10;
int x[2 * level + 1]; // OK
在定义一个位域长度时也是一样的。
int bf: W; // Firld width, W must be constant.
C++允许你将一个非const对象用const来初始化。下面的程序允许你在运行时初始化变量 level
,也因此,在编译时的level
不是一个const object,对应的表达式也不再是constant expression。
int n = 10;
int const level = n; // Ok
int x[2 * level + 1]; // Error, this is not a constant expression
constexpr
就是在这种情况下诞生的,一个 constexpr
的对象必须用一个 constant expression 来初始化:
int n = 10;
constexpr int m = n; // Not ok, n is not a constant expression.
constexpr int l = 10; // Ok, 10 is a const expression.
每个对象和函数的声明都有两个部分:declaration specifiers 和 declarator。在下面的例子中前面一长串static unsigned long int
都是declaration specifiers,后面的*x[N]
是declarator。
static unsigned long int *x[N];
Declaration specifiers进一步又分为type specifier(int
、unsigned
、long
等)和non-type specifier(extern
、static
、inline
等)。
Declarator是一个declarator-id,围绕着一些operators。上面的例子中,x
就是这个declarator-id,*
和[]
都属于operator。这些operators有一定的优先级关系。
Precedence | Operator | Meaning |
---|---|---|
Highest | ( ) |
Grouping |
[ ] |
Array | |
( ) |
Function | |
Lowest | * |
Pointer |
& |
Lvalue ref | |
&& |
Rvalue ref |
根据优先级关系,我们能够轻易说出后面的*x[N]
是一个array of pointers。而我们很容易能够想清楚,(*x)[N]
就是一个指针指向一个array of N integers。
我们提到过,Declaration specifiers进一步又分为type specifier和non-type specifier。它们分别的作用是什么?假如我们有下面的声明:
static unsigned long int *x[N];
其中static
是修饰declarator-idx
的,其他的type specifiers都是修饰其他的type specifier。所以下面的几种表达都是一样的:
const unsigned int x;
unsigned int const x;
unsigned const int x;
int const unsigned x;
在我们之前学习的过程中,常常会困惑于是指向常量的指针?还是常指针指向一个变量?我们需要注意:const
是一个type specifier,而constexpr
是一个non-type specifier。
constexpr unsigned long int *x[N]; // 指向变量数组的常指针
const unsigned long int *x[N]; // 指向常量的数组指针
unsigned long int *const x[N]; // 指向变量数组的常指针
非const
类型可以转换成const
类型的变量,但是反过来并不成立。const
相当于一种保证,使用变量的函数或表达式(借),可以使用const
保证原先的变量(被借)不被改变。但是反过来,如果变量本来就不允许被改变,但是函数或表达式不提供不被改变的保证,那么就会出错。
int ver;
const int const_ver = 10;
int add1(const int const_ver, const int ver); // ok
int add2(const int const_ver, int ver); // ok
int add3(int const_ver, int ver); // error
add a const is ok, but not to lose it.(同样适用于volatile, const-volatile 被称为CV qulifier)