關鍵字 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 變數
const
和 constexpr
變數之間的主要區別在於,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
函式:
它可能包含
if
和switch
陳述式,以及所有迴圈語句,包括for
、範圍型for
、while
和 do-while。它可能包含局部變數宣告,但必須初始化變數。 它必須是常值類型,而且不能是
static
或本機執行緒。 本機宣告的變數不需要是const
,而且可能會變動。constexpr
非static
成員函式不需要隱含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 或更新版本。