先說結論
相同點:const和consexpr都是用來定義常量的。
不一樣點:const聲明的常量,初始值引用的對象不必定是一個常量;constexpr聲明的常量,初始值必定是常量表達式。ios
constexpr是c++11標準添加的關鍵字。c++
之因此說const聲明的常量,初始值不必定是一個常量,主要是從引用和指針的角度出發的。若是初始化const時候,指定一個固定的字面值常量,那麼它引用的確定是常量。spa
const int i = 10; constexpr int j = 20;
這種狀況下,i和j都是常量,並且它們引用的也是一個常量(由於是固定的字面值)。那麼若是它們引用的不是固定的字面值,而是指針和引用呢?接下來筆者將從引用和指針的角度出發,解釋const和constexpr的區別:指針
經過以下語法能夠聲明一個常量int類型引用:c++11
const int &v;
注意這裏的v是一個常量int引用,也就是說,v是確定是一個int類型的常量引用(值不能改變),應該給它賦值常量int類型,若是咱們給它賦值變量int類型會怎麼樣呢?看下面的案例。
例如:code
#include <iostream> using namespace std; int main(){ int a = 20; const int &b = a;//引用a,常量int引用b引用了很是量inta 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認爲本身引用的是一個常量,實際卻不是)。blog
能夠經過以下的方式,來聲明一個常量指針。ci
int *const p;
p首先是一個常量,而後再是一個指針,而且這個指針指向一個int類型。
下面的案例io
#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只能提供讀數據,因此不經過。
上面筆者總結的規律還有一些須要補充,在不改變const對象的操做中還有一種是初始化,若是一個利用對象去初始化另一個對象(引用和指針除外,這裏主要是指拷貝),則他們是否是const都可有可無:
int i = 42; const int ci = i; // 正確:i的值被拷貝給了ci int j = ci; //正確 : ci的值被拷貝給了j
儘管ci是const類型,j是int類型。ci的常量特徵僅僅在執行改變ci的操做時纔會發揮做用,當用ci初始化j時,更本無需在乎ci是否是一個常量。拷貝一個對象的值不會改變它,一旦拷貝完成,新的對象和原來的對象就沒什麼關係了。
在上面的說過了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不是一個常量表達式