constexprC++11引入的關鍵字,用於編譯時的常數與常數函式。

宣告為constexpr函式的意義是:如果其參數均為合適的編譯時期常數,則對這個constexpr函式的呼叫就可用於期望常數表達式的場合(如模板的非類型參數,或列舉(enum)常數的值)。如果參數的值在執行時期才能確定,或者雖然參數的值是編譯時期常數,但不符合這個函式的要求,則對這個函式呼叫的求值只能在執行時期進行。

簡介

C++編譯時可確定常數表達式的結果,因此可在編譯時最佳化。C++規範在一些地方要求使用常數表達式,如宣告陣列的維度。但常數表達式不允許包含函式呼叫或者對象構造。因此下述程式碼無效:

int get_five() {return 5;}

int some_value[get_five() + 7]; // 创建包含12个整数的数组. C++03中非法,因为get_five() + 7不是常量表达式

C++11引入了關鍵字constexpr,允許程式設計者保證函式或對象的建構式是編譯時常數。[1]上述程式碼可以覆寫為:

constexpr int get_five() {return 5;}

int some_value[get_five() + 7]; // Create an array of 12 integers. Valid C++11

constexpr函式必須滿足下述限制:

  • 函式回傳值不能是void型別
  • 函式體不能宣告變數或定義新的型別
  • 函式體只能包含宣告、null陳述式或者一段return陳述式
  • 在形參實參結合後,return陳述式中的表達式為常數表達式

C++11去掉了const variable必須是整數型別(Integral Type)或者列舉型別的限制,只要是用於關鍵字constexpr定義即可:

constexpr double earth_gravitational_acceleration = 9.8;
constexpr double moon_gravitational_acceleration = earth_gravitational_acceleration / 6.0;

這些variable必須用常數表達式初始化。

為建構用戶定義類型的常數表達式,建構式必須用constexpr宣告,函式體僅包含宣告或null陳述式,不能宣告變數或定義資料型別。因此,建構式的實參值應該是常數表達式,直接初始化類別的資料成員。解構函式是平凡的。類型的複製建構式應該也定義為constexpr,以允許constexpr函式返回一個該類型的對象。類型的成員函式都應該是constexpr

constexpr函式或建構式的實參值如果不是常數表達式,那麼呼叫行為與結果就不是常數表達式。

C++14放寬了這些限制。宣告為constexpr的函式可以含有以下內容:[2]

  • 任何宣告,除了:
    • staticthread_local變數。
    • 沒有初始化的變數宣告。
  • 條件分支陳述式ifswitch
  • 所有的迴圈陳述式,包括基於範圍的for迴圈。
  • 表達式可以改變一個對象的值,只需該對象的生命週期在宣告為constexpr的函式內部開始。包括對有constexpr宣告的任何非const非靜態成員函式的呼叫。

goto仍然不允許在constexpr函式中出現。

constexpr支援編譯時期的遞迴。例如,可以寫一個constexpr函式計算斐波那契數列。

此外,C++11指出,所有被宣告為constexpr的非靜態成員函式也隱含宣告為const(即函式不能修改*this的值)。C++14已經刪除此點,非靜態成員函式可以為非const[3]

範例程式碼

// C++98/03
template <int N>
struct Factorial_Cpp03
{
    const static int value = N * Factorial_Cpp03<N - 1>::value;
};
template <>
struct Factorial_Cpp03<0>
{
    const static int value = 1;
};

// C++11
constexpr int factorial_Cpp11(int n)
{
    return n == 0 ? 1 : n * factorial_Cpp11(n - 1);
}

// C++14
constexpr int factorial_Cpp14(int n)
{
    int result = 1;
    for (int i = 1; i <= n; ++i)
        result *= i;
    return result;
}

int main()
{
    static_assert(Factorial_Cpp03<3>::value == 6, "error");
    static_assert(factorial_Cpp11(3) == 6, "error");
    static_assert(factorial_Cpp14(3) == 6, "error");
    return 0;
}

參考文獻

  1. ^ Gabriel Dos Reis; Bjarne Stroustrup. General Constant Expressions for System Programming Languages, Proceedings SAC ’10 (PDF). 22 March 2010 [2018-03-29]. (原始內容 (PDF)存檔於2018-06-13). 
  2. ^ Wong, Michael. The View form the C++ Standard meeting April 2013 Part 1. C/C++ Cafe. [14 June 2013]. (原始內容存檔於2013-10-13). 
  3. ^ N3652 Relaxing constraints on constexpr functions. [2018-03-29]. (原始內容存檔於2013-08-25).