【C++】C++中const與constexpr的比較
先說結論
相同點:const和consexpr都是用來定義常量的。
不同點:const宣告的常量,初始值引用的物件不一定是一個常量;constexpr宣告的常量,初始值一定是常量表達式。
constexpr是c++11標準新增的關鍵字。
之所以說const宣告的常量,初始值不一定是一個常量,主要是從引用和指標的角度出發的。如果初始化const時候,指定一個固定的字面值常量,那麼它引用的肯定是常量。
const int i = 10; constexpr int j = 20;
這種情況下,i和j都是常量,而且它們引用的也是一個常量(因為是固定的字面值)。那麼如果它們引用的不是固定的字面值,而是指標和引用呢?接下來筆者將從引用和指標的角度出發,解釋const和constexpr的區別:
const與引用
通過如下語法可以宣告一個引用int型別的常量引用:
const int &v;
注意這裡v是一個常量引用,也就是說,v是肯定是一個常量(值不能改變),但是v引用的int型別不能確定是否是常量。
例如:
#include <iostream> using namespace std; int main(){ int a = 20; const int &b = a;//引用a,常量引用b引用了非常量a cout << "a = " << a << ", b =" << b << endl; a = 10;//可以通過a改變變數的值 cout << "a = " << a << ", b = " << b << endl; //b = 20;//出錯,不可以通過b改變變數的值,因為b一個常量引用,所以不能通過b去改變。 return 0; }
結果:
a = 20, b = 20
a = 10, b = 10
上面的案例中,a是一個變數,b是一個常量引用。a變數的值不能通過b來改變,但是可以通過a來改變,因為a不是常量,b是常量引用(b認為自己引用的是一個常量,實際卻不是)。
const與指標
可以通過如下的方式,來宣告一個常量指標。
int *const p;
p首先是一個常量,然後再是一個指標,並且這個指標指向一個int型別。
下面的案例
#include <iostream> using namespace std; int main(){ int i = 10; int *const p = &i;//指向非常量的常量指標p,指向了非常量i cout << "i = " << i << ",*p = " << *p << endl; i = 20; cout << "i = " << i << ",*p = " << *p << endl; *p = 30; cout << "i = " << i << ",*p = " << *p << endl; int j = 0; //p = &j;//出錯 return 0; }
輸出
i = 10,*p = 10
i = 20,*p = 20
i = 30,*p = 30
上面的案例中p是一個常量型別的指標,並且指向一個非常量int物件。由於p是指標,所以*p解地址後實際上是變數i,所以可以通過*p改變變數的值。但是p = &j語句,會改變p變數中儲存的地址(初始化時儲存的是變數i的地址),由於p是常量,所以p中的值是不能改變,因此p = &j會報錯。
當然可以這樣定義
const int *const p;
這樣的話,p是一個常量指標,並且指向常量int型別。
例如:
#include <iostream> using namespace std; int main(){ int i = 10; const int *const p = &i;//指向常量int的常量指標p,指向了非常量i cout << "i = " << i << ",*p = " << *p << endl; i = 20; cout << "i = " << i << ",*p = " << *p << endl; //*p = 30;//出錯 int j = 0; //p = &j;//出錯 return 0; }
輸出結果:
i = 10,*p = 10
i = 20,*p = 20
雖然常量指標p應該指向一個常量int型別,但是筆者給它一個非常量int型別的地址,這樣一來,p會認為它指向的是一個常量int型別,所以當通過*p = 30改變它的值時,不能通過。但是通過i依然可以修改。
小結:
常量引用可以引用非常量,非常用引用不能引用常用。
指向常量的指標可以指向一個非常量,指向非常量的指標不能指向常量。
從邏輯上可以這樣理解:因為非常量具有可讀可寫的功能,常量只有可讀的功能;當常量引用非常量時,常量只期望可以讀資料,非常量不僅提供了讀資料,而且非常量還可以寫資料(修改),因此常量引用非常量可以滿足常量的需求,可以通過;返過來,常量不能夠滿足非常量的需求,所以不能通過。
int a = 10; const int &b = a;//正確,b只需要讀資料,a可以讀資料,所以可以通過。 const int c = 10; int &d = c;//錯誤,d需要讀資料和寫資料,c只能提供讀資料,所以不通過。 int e = 10; const int *f = &e;//正確,*f只需要能夠讀資料,e可以讀資料,所以可以通過。 const int g = 10; int *h = &g;//錯誤,*h需要讀資料和寫資料,g只能提供讀資料,所以不通過。
constexpr
在上面的說過了const的特點,可以得出,當const變數引用或指向某個變數時,被引用或指向的變數是不能確定是否是一個常量的。
C++11標準提供了constexpr關鍵字,被constexpr修飾的變數的初始值必須是常量表達式,也就是說,被constexpr修飾的變數,肯定是常量,而且引用常量表達式。
constexpr int m = 10;//20是常量表達式
constexpr int n = m + 1;//m+1是一個常量表達式
constexpr const int *p = &m;//錯誤,&m不是一個常量表達式
constexpr const int &r = m;//錯誤,m不是一個常量表達式