標籤:

C++: const and constexpr

C++: const and constexpr

const修飾變數

如上節所述,用const修飾變數的語義是要求編譯器去阻止所有對該變數的賦值行為。因此,必須在const變數初始化時就提供給它初值:

const int i;

i = 5; // !error

const int j = 10; // ok

這個初值可以是編譯時即確定的值,也可以是運行期才確定的值。如果給整數類型的const變數一個編譯時初值,那麼可以用這個變數作為聲明數組時的長度:

const int COMPILE_CONST = 10;

const int RunTimeConst = cin.get();

int a1[COMPLIE_CONST]; // ok in C++ and error in C

int a2[RunTimeConst]; // !error in C++

因為C++編譯器可以將數組長度中出現的編譯時常量直接替換為其字面值,相當於自動的宏替換。(gcc驗證發現,只有數組長度那裡直接做了替換,而其它用COMPILE_CONST賦值的地方並沒有進行替換。)

文件域的const變數默認是文件內可見的,如果需要在b.cpp中使用a.cpp中的const變數M,需要在M的初始化處增加extern

//a.cppextern const int M = 20; //b.cppextern const int M;

一般認為將變數的定義放在.h文件中會導致所有include該.h文件的.cpp文件都有此變數的定義,在鏈接時會造成衝突。但將const變數的定義放在.h文件中是可以的,編譯器會將這個變數放入每個.cpp文件的匿名namespace中,因而屬於是不同變數,不會造成鏈接衝突。(注意:但如果頭文件中的const量的初始值依賴於某個函數,而每次調用此函數的返回值不固定的話,會導致不同的編譯單元中看到的該const量的值不相等。猜測:此時將該const量作為某個類的static成員可能會解決此問題。)

const修飾指針與引用

const修飾引用時,其意義與修飾變數相同。但const在修飾指針時,規則就有些複雜了。

簡單的說,可以將指針變數的類型按變數名左邊最近的『*』分成兩部分,右邊的部分表示指針變數自己的性質,而左邊的部分則表示它指向元素的性質:

const int *p1; // p1 is a non-const pointer and points to a const int

int * const p2; // p2 is a const pointer and points to a non-const int

const int * const p3; // p3 is a const pointer and points to a const it

const int *pa1[10]; // pa1 is an array and contains 10 non-const pointer point to a const int

int * const pa2[10]; // pa2 is an array and contains 10 const pointer point to a non-const int

const int (* p4)[10]; // p4 is a non-const pointer and points to an array contains 10 const int

const int (*pf)(); // pf is a non-const pointer and points to a function which has no arguments and returns a const int

...

const指針的解讀規則差不多就是這些了……

指針自身為const表示不可對該指針進行賦值,而指向物為const則表示不可對其指向進行賦值。因此可以將引用看成是一個自身為const的指針,而const引用則是const Type * const指針。

指向為const的指針是不可以賦值給指向為非const的指針,const引用也不可以賦值給非const引用,但反過來就沒有問題了,這也是為了保證const語義不被破壞。

可以用const_cast來去掉某個指針或引用的const性質,或者用static_cast來為某個非const指針或引用加上const性質:

int i;

const int *cp = &i;

int *p = const_cast<int *>(cp);

const int *cp2 = static_cast<const int *>(p); // here the static_cast is optional

C++類中的this指針就是一個自身為const的指針,而類的const方法中的this指針則是自身和指向都為const的指針。

--------

constexpr是C++11中新增的關鍵字,其語義是「常量表達式」,也就是在編譯期可求值的表達式。最基礎的常量表達式就是字面值或全局變數/函數的地址或sizeof等關鍵字返回的結果,而其它常量表達式都是由基礎表達式通過各種確定的運算得到的。constexpr值可用於enum、switch、數組長度等場合。

constexpr所修飾的變數一定是編譯期可求值的,所修飾的函數在其所有參數都是constexpr時,一定會返回constexpr。

constexpr int Inc(int i) { return i + 1;} constexpr int a = Inc(1); // okconstexpr int b = Inc(cin.get()); // !errorconstexpr int c = a * 2 + 1; // ok

constexpr還能用於修飾類的構造函數,即保證如果提供給該構造函數的參數都是constexpr,那麼產生的對象中的所有成員都會是constexpr,該對象也就是constexpr對象了,可用於各種只能使用constexpr的場合。注意,constexpr構造函數必須有一個空的函數體,即所有成員變數的初始化都放到初始化列表中。

struct A { constexpr A(int xx, int yy): x(xx), y(yy) {} int x, y;}; constexpr A a(1, 2);enum {SIZE_X = a.x, SIZE_Y = a.y};

constexpr的好處:

  1. 是一種很強的約束,更好地保證程序的正確語義不被破壞。
  2. 編譯器可以在編譯期對constexpr的代碼進行非常大的優化,比如將用到的constexpr表達式都直接替換成最終結果等。
  3. 相比宏來說,沒有額外的開銷,但更安全可靠。

--------------

談談 C++ 的編譯期計算

const 變數

const修飾變數,表示這個變數是不可修改,const變數必須初始化,一經初始化就不可修改:

  • 編譯時初始化。

const int SIZE = 100;

  • 運行時初始化。

vector<int> v;const int i = v.size();

constexpr 變數

const變數的值可以在編譯時或運行時確定,與const相比,constexpr的限制更多,因為constexpr變數的值必須在編譯時就能確定。

  在一些場合之下,變數的值要求是編譯期就必須確定的,constexpr變數正好滿足要求:

  • 數組的大小必須是編譯期常量。
  • std::array的大小必須是編譯期常量。
  • std::bitset的大小必須是編譯期常量。

constexpr auto SIZE = 100;std::array<int, SIZE> arr;

constexpr 函數

constexpr函數則與編譯期計算有關,要是constexpr函數所使用的變數其值能夠在編譯時就確定,那麼constexpr函數就能在編譯時執行計算。另一方面,要是constexpr函數所使用的變數其值只能在運行時確定,那麼constexpr就和一般的函數沒區

constexpr function: 在程序編譯的時候就已經執行好了。代價是增加編譯時間,但程序能執行得更高效。

struct S { constexpr S(int);};const S s0(0);constexpr S s1(1);

s0 is a constant, but it does not promise to be initialized at compile-time. s1 is marked constexpr, so it is a constant and, because Ss constructor is also marked constexpr, it will be initialized at compile-time.

Mostly this matters when initialization at runtime would be time-consuming and you want to push that work off onto the compiler, where its also time-consuming, but doesnt slow down execution time of the compiled program

推薦閱讀:

C 語言比 C++ 更強大嗎?
C++中lambda與function的推導問題?
C++序列化json字元串對Unicode有哪些特殊處理?

TAG:編程 | 數學 | C |