天天看點

C++ const 和 constexpr 的差別?

constexpr

是 C++11 引入的,一方面是為了引入更多的編譯時計算能力,另一方面也是解決 C++98 的

const

的雙重語義問題。

在 C 裡面,

const

很明确隻有 「隻讀」 一個語義,不會混淆。C++ 在此基礎上增加了 「常量」 語義,也由 const 關鍵字來承擔,引出來一些奇怪的問題。C++11 把 「常量」 語義拆出來,交給新引入的

constexpr

關鍵字。

示範一下

const

關鍵字的雙重語義可能帶來的的問題:

template<int N> class C{};

constexpr int FivePlus(int x) { return 5 + x; }

void f(const int x) {
    C<x> c1; // Error: x is not compile-time evaluable.
    C<FivePlus(6)> c2; // OK
}

void g() {
    const int x = 5;
    C<x> c1; // OK!!! 此處用x的「常量」語義
    *(int *)(&x) = 6; // Still OK! 隻處用x的「隻讀」語義,去除const後便可寫了; (int*)是強制類型轉換,有些編譯器可能會報錯
    // 如果上一句報錯,就可以寫成下面這一句:
    // *const_cast<int*>(&x) = 6;
    C<x> c2; // Still OK! c2是C<5>類型(不是C<6>!)
    C<FivePlus(x)> c3; // Still OK! c3是C<10>類型(不是C<11>!)

    printf("%d\n", x); // 此處絕大多數(所有?)C++編譯器會輸出5!!
                       // (然而,如果用一個C編譯器來編譯類似代碼,一定輸出6)
    const int* p = &x;
    printf("%d\n", *p); // 此處,大多數C++編譯器輸出6
}
           

可以看到,f 和 g 都有一個 const int x,但它們的行為卻不同。原因在于:f 的 const int x 隻是「一個隻讀的變量」;而 g 的 const int x 既是「一個隻讀的變量」,又是「一個值為5的常量」,變得飄忽不定。

在 C++11 以後,建議凡是 「常量」 語義的場景都使用

constexpr

,隻對 「隻讀」 語義使用

const

可能有人就會糊塗了,隻讀變量 難道不就是 常量嗎?然則非也。

同樣一個記憶體位址,用常量指針關聯時,通過這一路徑就無法修改;換用非常量指針 關聯時,在這條路徑上就是可以修改的。

換句話說,用

const

限定變量時,隻是剝奪了通過該變量修改相應記憶體中内容的可能性,但是有可能其他程式或其他指向該記憶體位址的變量會改變這塊記憶體中的内容,也就是說這塊記憶體位址空間的内容并不會保證一直不變。是以,從現在開始,就把

const

了解成隻讀的限定符,把

constexpr

了解成常量的限定符。