constexpr 说明符 (C++11 起)
解釋
constexpr 說明符聲明可以在編譯時對實體求值。這些實體(給定了合適的函數實參的情況下)即可用於需要編譯期常量表達式的地方。
對象或非靜態成員函數(C++14 前)聲明中的 constexpr 說明符蘊含 const。
函數或靜態數據成員(C++17 起)首個聲明中的 constexpr 說明符蘊含 inline。如果函數或函數模板的一個聲明擁有 constexpr 說明符,那麼它的所有聲明都必須含有該說明符。
constexpr 變量
如果以下條件都滿足,那麼變量或變量模板(C++14 起)可聲明為 constexpr:
| (C++26 前) | |
|
(C++26 起) |
|
如果 |
(C++20 起) |
constexpr 函數
函數或函數模板可以聲明為 constexpr。
滿足以下全部條件時,函數適於 constexpr:
|
(C++20 前) |
|
(C++23 前) |
|
(C++26 前) |
|
(C++20 起) |
|
(C++14 前) | ||
|
(C++14 起) (C++23 前) |
除了實例化的 constexpr 函數之外,非模板化的 constexpr 函數必須適於 constexpr。
|
對於既未預置也未棄置的非構造函數 對於模板化的 |
(C++23 前) |
在給定語境中調用 constexpr 函數所產生的結果,與在相同語境中調用等價的非 constexpr 函數的結果,除了以下方面外都相同:
constexpr 構造函數
|
在
|
(C++26 前) | ||||
|
構造函數適於 constexpr 的條件與其他函數一樣。 |
(C++26 起) |
|
對於既不是預置的也未被模板化的 |
(C++23 前) |
constexpr 析構函數
|
析構函數不能是 |
(C++20 前) | ||
|
在
|
(C++20 起) (C++26 前) | ||
|
析構函數適於 constexpr 的條件與其他函數一樣。 |
(C++26 起) |
註解
|
因為 constexpr int f();
constexpr bool b1 = noexcept(f()); // false,constexpr 函数未定义
constexpr int f() { return 0; }
constexpr bool b2 = noexcept(f()); // true,f() 是常量表达式
|
(C++17 前) |
|
可以寫出所有調用都不滿足核心常量表達式要求的 void f(int& i) // 不是 constexpr 函数
{
i = 0;
}
constexpr void g(int& i) // C++23 起良构
{
f(i); // 无条件调用 f,不可能是常量表达式
}
|
(C++23 起) |
constexpr 構造函數允許用於非字面類型的類。例如,std::shared_ptr 的默認構造函數是 constexpr 的,允許進行常量初始化。
引用變量可聲明為 constexpr(它的初始化式必須是引用常量表達式):
static constexpr int const& x = 42; // 到 const int 对象的 constexpr 引用
// (该对象拥有静态存储期,因为静态引用延长了生存期)
|
儘管在 如果變量擁有常量析構,那麼無需為調用它的析構函數而生成機器碼,即使它的析構函數不平凡。 非 lambda、非特殊成員且非模板化的 |
(C++20 起) |
| 功能特性測試宏 | 值 | 標準 | 功能特性 |
|---|---|---|---|
__cpp_constexpr |
200704L |
(C++11) | constexpr
|
201304L |
(C++14) | 放寬 constexpr、非 const 的 constexpr 方法
| |
201603L |
(C++17) | constexpr lambda 表達式
| |
201907L |
(C++20) | constexpr 函數中的平凡默認初始化和匯編聲明
| |
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 函數中的動態存儲期操作
|
__cpp_constexpr_virtual_inheritance |
202506L |
(C++26) | constexpr 虛繼承
|
關鍵詞
示例
定義 C++11/14 的 constexpr 函數用以計算階乘;定義擴展字符串字面量的字面類型:
#include <iostream>
#include <stdexcept>
// C++11 constexpr 函数使用递归而非迭代
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
// C++14 constexpr 函数可使用局部变量和循环
#if __cplusplus >= 201402L
constexpr int factorial_cxx14(int n)
{
int res = 1;
while (n > 1)
res *= n--;
return res;
}
#endif // C++14
// 字面类
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 函数通过抛异常来提示错误
// C++11 中,它们必须用条件运算符 ?: 来这么做
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 函数必须把一切放在单条 return 语句中
// (C++14 无此要求)
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);
}
// 输出要求编译时常量的函数,用于测试
template<int n>
struct constN
{
constN() { std::cout << n << '\n'; }
};
int main()
{
std::cout << "4! = ";
constN<factorial(4)> out1; // 在编译时计算
volatile int k = 8; // 使用 volatile 防止优化
std::cout << k << "! = " << factorial(k) << '\n'; // 运行时计算
std::cout << "\"Hello, world!\" 里小写字母的个数是 ";
constN<countlower("Hello, world!")> out2; // 隐式转换为 conststr
constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
constexpr int length_a = sizeof a / sizeof(int); // C++17 中为 std::size(a),
// C++20 中为 std::ssize(a)
std::cout << "长度为 " << length_a << " 的数组中各元素为:";
for (int i = 0; i < length_a; ++i)
std::cout << a[i] << ' ';
std::cout << '\n';
}
輸出:
4! = 24
8! = 40320
"Hello, world!" 里小写字母的个数是 9
长度为 12 的数组中各元素为: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 | 函數體是 = default 或 = delete 的constexpr 構造函數的類可以有虛基類
|
這些類也不能有虛基類 |
| CWG 1595 | C++11 | constexpr 委託構造函數要求涉及的所有構造函數都是 constexpr 函數
|
僅要求目標構造函數是 constexpr 函數
|
| CWG 1712 | C++14 | constexpr 變量模板的所有聲明都需要包含 constexpr 說明符[1]
|
不再需要 |
| CWG 1911 | C++11 | 非字面類型不允許擁有 constexpr 構造函數
|
在常量初始化中允許 |
| CWG 2004 | C++11 | 在常量表達式中允許複製/移動有 mutable 成員的聯合體 | mutable 變體現在無法被隱式複製/移動 |
| CWG 2022 | C++98 | 等價的 constexpr 和非 constexpr 函數是否產生相等結果可能會取決於是否進行複製消除 |
假設始終會進行複製消除 |
| CWG 2163 | C++14 | constexpr 函數中禁止 goto 語句,但允許標號
|
標號也被禁止 |
| CWG 2268 | C++11 | CWG 問題 2004 禁止了複製/移動有 mutable 成員的聯合體 | 在該對象在常量表達式中創建的情況下允許 |
| CWG 2278 | C++98 | CWG 問題 2022 的解決方案無法實現 | 假設始終不會進行複製消除 |
| CWG 2531 | C++11 | 當非內聯變量以 constexpr 重新聲明後變為內聯
|
不會變為內聯 |
- ↑ 此要求是多餘的,因為每個變量模板最多只有一條帶有
constexpr說明符的聲明。
參閱
| 常量表達式 | 定義可在編譯時求值的表達式 |
consteval 說明符 (C++20)
|
指定函數為立即函數,即對該函數的每次調用必須在常量求值中進行 |
constinit 說明符 (C++20)
|
斷言變量擁有靜態初始化,即零初始化與常量初始化 |
constexpr 的 C 文檔
| |