constexpr
說明符 (C++11 起)
目錄 |
[編輯] 解釋
constexpr 說明符宣告實體的值可以在編譯時求值。然後,這些實體可以在只允許編譯時常量表達式的地方使用(前提是給出了適當的函式引數)。
用於物件宣告或非靜態成員函式(C++14 前)的 constexpr 說明符隱含 const。
用於函式或靜態資料成員(C++17 起)的第一個宣告中的 constexpr 說明符隱含 inline。如果函式或函式模板的任何宣告具有 constexpr 說明符,則每個宣告都必須包含該說明符。
[編輯] constexpr 變數
變數或變數模板(C++14 起)可以宣告為 constexpr,如果滿足以下所有條件:
(直到 C++26) | |
|
(C++26 起) |
如果一個 constexpr 變數不是翻譯單元區域性的,它不應被初始化為引用可在常量表達式中使用的翻譯單元區域性實體,也不應具有引用此類實體的子物件。在模組介面單元(在其私有模組片段之外,如果有)或模組分割槽中不允許此類初始化,並在任何其他上下文中被棄用。 |
(C++20 起) |
[編輯] constexpr 函式
函式或函式模板可以宣告為 constexpr。
函式是 constexpr-suitable 的,如果滿足以下所有條件:
- 如果它是建構函式或解構函式(C++20 起),其類沒有任何虛基類。
|
(C++20 前) |
|
(直至 C++23) |
|
(C++20 起) |
|
(C++14 前) | ||
|
(C++14 起) (直至 C++23) |
除了例項化 constexpr 函式外,非模板 constexpr 函式必須是 constexpr-suitable 的。
對於非建構函式 constexpr 函式,如果既未預設也未模板化,如果不存在任何引數值使得函式呼叫可以是核心常量表達式的求值子表示式,則程式格式錯誤,不需要診斷。 對於模板化的 constexpr 函式,如果沒有函式/類模板的特化在被視為非模板化函式時能使模板化函式 constexpr-suitable,則程式格式錯誤,不需要診斷。 |
(直至 C++23) |
在給定上下文中呼叫 constexpr 函式會產生與在相同上下文中呼叫等效的非 constexpr 函式相同的結果,除了以下情況:
[編輯] constexpr 建構函式
除了 constexpr 函式的要求外,建構函式還需要滿足以下所有條件才能是 constexpr-suitable:
|
(直至 C++23) |
- 該類沒有任何虛基類。
對於既未預設也未模板化的 constexpr 建構函式,如果不存在任何引數值使得函式呼叫可以是某個受常量表達式約束的物件的初始化完整表示式的求值子表示式,則程式格式錯誤,不需要診斷。 |
(直至 C++23) |
[編輯] constexpr 解構函式
解構函式不能是 constexpr,但平凡解構函式可以在常量表達式中隱式呼叫。 |
(C++20 前) | ||
除了 constexpr 函式的要求外,解構函式還需要滿足以下所有條件才能是 constexpr-suitable:
|
(C++20 起) |
[編輯] 注意
由於 constexpr int f(); constexpr bool b1 = noexcept(f()); // false, undefined constexpr function constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true, f() is a constant expression |
(C++17 前) |
可以編寫一個 constexpr 函式,其呼叫永遠不能滿足核心常量表達式的要求。 void f(int& i) // not a constexpr function { i = 0; } constexpr void g(int& i) // well-formed since C++23 { f(i); // unconditionally calls f, cannot be a constant expression } |
(C++23 起) |
對於非字面型別類,允許使用 constexpr 建構函式。例如,std::shared_ptr 的預設建構函式是 constexpr,允許常量初始化。
引用變數可以宣告為 constexpr(其初始化器必須是引用常量表達式)。
static constexpr int const& x = 42; // constexpr reference to a const int object // (the object has static storage duration // due to life extension by a static reference)
儘管 try 塊和內聯彙編在 constexpr 函式中是允許的,但在常量表達式中仍然禁止丟擲未捕獲的(C++26 起)異常或執行彙編。 如果變數具有常量銷燬,則無需生成機器碼來呼叫其解構函式,即使其解構函式不是平凡的。 非 lambda、非特殊成員和非模板化的 constexpr 函式不能隱式地成為即時函式。使用者需要顯式地將其標記為 consteval 才能使此類預期函式定義格式正確。 |
(C++20 起) |
功能測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_constexpr |
200704L |
(C++11) | constexpr |
201304L |
(C++14) | 放寬的 constexpr,非 const constexpr 方法 | |
201603L |
(C++17) | Constexpr lambda | |
201907L |
(C++20) | constexpr 函式中的平凡預設初始化和asm-declaration | |
202002L |
(C++20) | 常量求值中聯合體活躍成員的改變 | |
202110L |
(C++23) | constexpr 函式中的非字面量變數、標籤和goto 語句 | |
202207L |
(C++23) | 放寬一些 constexpr 限制 | |
202211L |
(C++23) | 允許在 constexpr 函式中使用 static constexpr 變數 | |
202306L |
(C++26) | 從 void* 的 constexpr 轉換:走向 constexpr 型別擦除 | |
__cpp_constexpr_in_decltype |
201711L |
(C++11) (DR) |
當常量求值需要時,函式和變數定義的生成 |
__cpp_constexpr_dynamic_alloc |
201907L |
(C++20) | constexpr 函式中的動態儲存持續時間操作 |
[編輯] 關鍵詞
[編輯] 示例
定義 C++11/14 constexpr 函式來計算階乘;定義擴充套件字串字面量的字面型別
#include <iostream> #include <stdexcept> // C++11 constexpr functions use recursion rather than iteration constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // C++14 constexpr functions may use local variables and loops #if __cplusplus >= 201402L constexpr int factorial_cxx14(int n) { int res = 1; while (n > 1) res *= n--; return res; } #endif // C++14 // A literal class class conststr { const char* p; std::size_t sz; public: template<std::size_t N> constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {} // constexpr functions signal errors by throwing exceptions // in C++11, they must do so from the conditional operator ?: constexpr char operator[](std::size_t n) const { return n < sz ? p[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz; } }; // C++11 constexpr functions had to put everything in a single return statement // (C++14 does not have that requirement) constexpr std::size_t countlower(conststr s, std::size_t n = 0, std::size_t c = 0) { return n == s.size() ? c : 'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) : countlower(s, n + 1, c); } // An output function that requires a compile-time constant, for testing template<int n> struct constN { constN() { std::cout << n << '\n'; } }; int main() { std::cout << "4! = "; constN<factorial(4)> out1; // computed at compile time volatile int k = 8; // disallow optimization using volatile std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time std::cout << "The number of lowercase letters in \"Hello, world!\" is "; constN<countlower("Hello, world!")> out2; // implicitly converted to conststr constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; constexpr int length_a = sizeof a / sizeof(int); // std::size(a) in C++17, // std::ssize(a) in C++20 std::cout << "Array of length " << length_a << " has elements: "; for (int i = 0; i < length_a; ++i) std::cout << a[i] << ' '; std::cout << '\n'; }
輸出
4! = 24 8! = 40320 The number of lowercase letters in "Hello, world!" is 9 Array of length 12 has elements: 0 1 2 3 4 5 6 7 8 0 0 0
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 1358 | C++11 | 模板化的 constexpr 函式也需要 至少有一個有效的引數值 |
不需要 |
CWG 1359 | C++11 | constexpr 聯合建構函式 必須初始化所有資料成員 |
對於非空聯合體,初始化恰好一個數據 成員 |
CWG 1366 | C++11 | 帶有 constexpr 建構函式的類,其函式體 是 = default 或 = delete 可以有虛基類 |
這樣的類既不能 有虛基類 |
CWG 1595 | C++11 | constexpr 委託建構函式要求 所有涉及的建構函式都是 constexpr |
只要求目標 建構函式是 constexpr |
CWG 1712 | C++14 | 一個 constexpr 變數模板被要求 所有宣告都包含 constexpr 說明符[1] |
不再需要 |
CWG 1911 | C++11 | 不允許非字面型別的 constexpr 建構函式 | 允許在常量初始化中 |
CWG 2004 | C++11 | 複製/移動帶可變成員的聯合體 在常量表達式中是允許的 |
可變變體取消 隱式複製/移動 |
CWG 2022 | C++98 | 等效的 constexpr 和非 constexpr 函式是否產生相等結果可能取決於 是否執行復制省略 |
假設複製省略始終在 常量表達式中執行 |
CWG 2163 | C++14 | constexpr 函式中允許標籤 即使 goto 語句被禁止 |
標籤也禁止 |
CWG 2268 | C++11 | 複製/移動帶可變成員的聯合體被 CWG 問題 2004 的決議禁止 |
如果物件是在 常量表達式中建立的,則允許 |
CWG 2278 | C++98 | CWG 問題 2022 的決議無法實現 | 假設複製省略從不執行 常量表達式中執行 |
CWG 2531 | C++11 | 非內聯變數變為內聯 如果它用 constexpr 重新宣告 |
變數不會 變為內聯 |
- ↑ 這是冗餘的,因為具有 constexpr 說明符的變數模板不能有多個宣告。
[編輯] 另請參閱
常量表達式 | 定義可在編譯時求值的表示式 |
consteval 說明符(C++20) |
指定函式是一個即時函式,即對函式的所有呼叫都必須在常量求值中 |
constinit 說明符(C++20) |
斷言變數具有靜態初始化,即零初始化和常量初始化 |
C 文件,關於 constexpr
|