1. 程式人生 > >【C++】C++中const與constexpr的比較

【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不是一個常量表達式