C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
constexpr 说明符(C++11 起)
constexpr
- 指定变量或函数的值可在常量表达式中出现
解释
constexpr
说明符声明可以在编译时求得函数或变量的值。然后这些变量和函数(若给定了合适的函数实参)即可用于仅允许编译时常量表达式之处。用于对象或非静态成员函数 (C++14 前)声明的 constexpr 说明符蕴含 const。用于函数声明的 constexpr 说明符或 static 成员变量 (C++17 起)蕴含 inline。若函数或函数模板的任何声明拥有 constexpr
说明符,则每个声明必须都含有该说明符。
constexpr 变量必须满足下列要求:
- 其类型必须是字面类型 (LiteralType) 。
- 它必须被立即初始化
- 其初始化的全表达式,包括所有隐式转换、构造函数调用等,都必须是常量表达式
|
(C++20 起) |
constexpr 函数必须满足下列要求:
|
(C++20 前) |
|
(C++20 起) |
- 其返回类型(若存在)必须是字面类型 (LiteralType)
- 其每个参数都必须是字面类型 (LiteralType)
- 对于构造函数与析构函数 (C++20 起),该类必须无虚基类
- 至少存在一组实参值,使得函数的一个调用为核心常量表达式的被求值的子表达式(对于构造函数为足以用于常量初始化器) (C++14 起)。不要求对这点的诊断。
|
(C++20 前) |
(C++14 前) | |||
|
(C++14 起) |
函数体非 =delete; 的 constexpr 构造函数必须满足下列额外要求:
|
(C++20 前) |
- 每个被选用于初始化非静态成员和基类的构造函数必须是 constexpr 构造函数。
析构函数不能为 constexpr ,但能在常量表达式中调用平凡析构函数。 |
(C++20 前) |
函数体非 =delete; 的 constexpr 析构函数必须满足下列额外要求:
|
(C++20 起) |
对于 constexpr 函数模板和类模板的 constexpr 函数成员,必须至少有一个特化满足上述要求。其他特化仍被认为是 constexpr,尽管常量表达式中不能出现这种函数的调用。
注解
因为 noexcept 运算符始终对常量表达式返回 constexpr int f(); constexpr bool b1 = noexcept(f()); // false,constexpr 函数未定义 constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true,f() 是常量表达式 |
(C++17 前) |
constexpr 构造函数允许用于非字面类型的类。例如,std::unique_ptr 的默认构造函数是 constexpr,允许常量初始化。
引用变量可声明为 constexpr(其初始化器必须是引用常量表达式):
static constexpr int const& x = 42; // 到 const int 对象的 constexpr 引用 // (该对象拥有静态存储期,因为 static 引用续命)
尽管在 constexpr 函数中允许 try 块与内联汇编,常量表达式中仍然不允许抛异常或执行汇编。 若 constexpr 变量拥有常量析构,则无需为调用其析构函数而生成机器码。 |
(C++20 起) |
关键词
示例
计算阶乘的 C++11 constexpr 函数的定义,及扩展字符串字面量的字面类型:
#include <iostream> #include <stdexcept> // C++11 constexpr 函数使用递归而非迭代 // (C++14 constexpr 函数可使用局部变量和循环) constexpr int factorial(int n) { return n <= 1? 1 : (n * factorial(n - 1)); } // 字面类 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 << "the number of lowercase letters in \"Hello, world!\" is "; constN<countlower("Hello, world!")> out2; // 隐式转换到 conststr }
输出:
4! = 24 8! = 40320 the number of lowercase letters in "Hello, world!" is 9
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
DR | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 1911 | C++14 | 不允许对于非字面类型的 constexpr 构造函数 | 在常量初始化中允许 |
CWG 2004 | C++14 | 在常量表达式中允许复制/移动有 mutable 成员的联合体 | 去除 mutable 变体隐式复制/移动的资格 |
CWG 2163 | C++14 | 尽管 goto 在 constexpr 函数中被禁止,标号却得到允许 | 标号也被禁止 |
CWG 2268 | C++14 | cwg 2004 曾禁止了复制/移动有 mutable 成员的联合体 | 若该对象在常量表达式中创建,则允许 |