constexpr
constexpr是C++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]
- 任何聲明,除了:
static
或thread_local
變量。- 沒有初始化的變量聲明。
- 條件分支語句
if
和switch
。 - 所有的迴圈語句,包括基於範圍的
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;
}
參考文獻
- ^ 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).
- ^ Wong, Michael. The View form the C++ Standard meeting April 2013 Part 1. C/C++ Cafe. [14 June 2013]. (原始內容存檔於2013-10-13).
- ^ N3652 Relaxing constraints on constexpr functions. [2018-03-29]. (原始內容存檔於2013-08-25).