共用方式為


constexpr (C++)

關鍵字 constexpr 是在 C++11 中推出,並在 C++14 中改進。 意思是常數運算式。 比如 const 可以應用於變數:當任何程式碼嘗試修改值時,都會引發編譯器錯誤。 與 const 不同,constexpr 也可以應用於函式和類別建構函式。 constexpr 指示該值或返回值是常數,並且盡可能在編譯時計算。

在需要 constexpr 整數 (例如在範本引數和陣列宣告中) 之處,便可以使用 const 整數值。 而且,當可以在編譯期間而不是執行階段計算值時,這有助於讓您的程式執行速度更快且使用較少的記憶體。

為了限制編譯時常數計算的複雜性及其對編譯時間的潛在影響,C++14 標準要求常數運算式中的類型為常值類型

語法

constexpr literal-typeidentifier=constant-expression;
constexpr literal-typeidentifier{constant-expression};
constexpr literal-typeidentifier(params);
constexpr ctor(params);

參數

params
一個或多個參數,每個參數都必須是常值類型,並且其本身必須是常數運算式。

傳回值

constexpr 變數或函式必須傳回常值類型

constexpr 變數

constconstexpr 變數之間的主要區別在於,const 變數的初始化可以推遲到執行階段。 constexpr 變數必須在編譯期間初始化。 所有 constexpr 變數都是 const

  • 如果變數具有常值類型並已初始化,則可以使用 constexpr 宣告。 如果初始化是由建構函式執行,則建構函式必須宣告為 constexpr

  • 符合這些條件時:所參考的物件已由常數運算式初始化,而且在初始化期間叫用的任何隱含轉換也是常數運算式,則參考可以宣告為 constexpr

  • constexpr 變數或函式的所有宣告都必須具有 constexpr 規範。

constexpr float x = 42.0;
constexpr float y{108};
constexpr float z = exp(5, 3);
constexpr int i; // Error! Not initialized
int j = 0;
constexpr int k = j + 1; //Error! j not a constant expression

constexpr 函式

constexpr 函式是一種其傳回值可以在取用程式碼需要它時,在編譯時間計算的函式。 取用程式碼需要編譯時間傳回的值來初始化 constexpr 變數,或提供非型別範本引數。 當其參數是 constexpr 值時,constexpr 函式會生成編譯時常數。 當呼叫時使用了非 constexpr 引數,或在編譯時間不需要其值時,便會像一般函式一樣在執行階段產生值。 (這種雙行為讓您不必撰寫相同函式的 constexpr 和非 constexpr 版本。)

constexpr 函式或建構函式隱含 inline

下列規則適用於 constexpr 函式:

  • constexpr 函式必須只接受並傳回常值類型

  • constexpr 函式可以是遞迴的。

  • 在 C++20 之前,constexpr 函式不能是虛擬的,並且當封閉類別具有任何虛擬基底類別時,不能將建構函式定義為 constexpr。 在 C++20 和更新版本中,constexpr 函式可以是虛擬的。 當您指定 constexpr 或更高版本編譯器選項時,Visual Studio 2019 版本 16.10 及更高版本支援 /std:c++20 虛擬函式。

  • 主體可以定義為 = default= delete

  • 本文不能包含任何 goto 陳述式或 try 區塊。

  • constexpr 範本的明確特製化可以宣告為 constexpr

  • constexpr 範本的明確特製化不需要也是 constexpr

下列規則適用於 Visual Studio 2017 和更新版本中的 constexpr 函式:

  • 它可能包含 ifswitch 陳述式,以及所有迴圈語句,包括 for、範圍型 forwhiledo-while

  • 它可能包含局部變數宣告,但必須初始化變數。 它必須是常值類型,而且不能是 static 或本機執行緒。 本機宣告的變數不需要是 const,而且可能會變動。

  • constexprstatic 成員函式不需要隱含 const

constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

提示

在 Visual Studio 偵錯工具中,偵錯非最佳化偵錯組建時,您可以藉由在函式中放入中斷點,來分辨 constexpr 函式是否會在編譯時間評估。 如果斷點被觸發,則表示函式在執行時被呼叫。 如果沒有,則會在編譯時呼叫函式。

extern constexpr

/Zc:externConstexpr 編譯器選項會導致編譯器將 外部連結 套用至使用 extern constexpr所宣告的變數。 在早期版本的 Visual Studio 中,無論是預設情況下還是指定 /Zc:externConstexpr- 時,Visual Studio 都會將內部鏈接應用於 constexpr 變數,即使使用 extern 關鍵字也是如此。 /Zc:externConstexpr 選項已在 Visual Studio 2017 更新 15.6 中開始提供,並預設為關閉。 /permissive- 選項不會啟用 /Zc:externConstexpr

範例

下列範例會顯示 constexpr 變數、函式及使用者定義類型。 在 main() 的最後一個陳述式中,constexpr 成員函式 GetValue() 是個執行階段呼叫,因為編譯時間不需要知道該值。

// constexpr.cpp
// Compile with: cl /EHsc /W4 constexpr.cpp
#include <iostream>

using namespace std;

// Pass by value
constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

// Pass by reference
constexpr float exp2(const float& x, const int& n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp2(x * x, n / 2) :
        exp2(x * x, (n - 1) / 2) * x;
}

// Compile-time computation of array length
template<typename T, int N>
constexpr int length(const T(&)[N])
{
    return N;
}

// Recursive constexpr function
constexpr int fac(int n)
{
    return n == 1 ? 1 : n * fac(n - 1);
}

// User-defined type
class Foo
{
public:
    constexpr explicit Foo(int i) : _i(i) {}
    constexpr int GetValue() const
    {
        return _i;
    }
private:
    int _i;
};

int main()
{
    // foo is const:
    constexpr Foo foo(5);
    // foo = Foo(6); //Error!

    // Compile time:
    constexpr float x = exp(5, 3);
    constexpr float y { exp(2, 5) };
    constexpr int val = foo.GetValue();
    constexpr int f5 = fac(5);
    const int nums[] { 1, 2, 3, 4 };
    const int nums2[length(nums) * 2] { 1, 2, 3, 4, 5, 6, 7, 8 };

    // Run time:
    cout << "The value of foo is " << foo.GetValue() << endl;
}

需求

Visual Studio 2015 或更新版本。

另請參閱

宣告和定義
const