Namespaces
Variants

Constant expressions

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

定义一个可在编译时求值的 expression (表达式)。

此类表达式可用作常量模板参数、数组大小,以及其他需要常量表达式的场景,例如。

int n = 1;
std::array<int, n> a1;  // 错误:“n”不是常量表达式
const int cn = 2;
std::array<int, cn> a2; // 正确:“cn”是常量表达式

目录

定义

属于以下任一常量表达式类别的表达式即为 常量表达式

C++98 常量表达式类别

整型常量表达式 (C++98)

在以下场景中,C++ 要求表达式必须能求值为整型或枚举常量:

满足以下所有条件的表达式即为 整型常量表达式

  • 仅涉及以下实体:
  • 算术类型的字面量
  • 枚举项
  • 满足以下所有条件的变量或静态数据成员:
  • 经 const 限定
  • 未经 volatile 限定
  • 为整型或枚举类型
  • 使用常量表达式初始化
  • 不使用浮点数字面量,除非将其 显式转换 为整型或枚举类型
  • 不应用任何向非整型和非枚举类型的转换
  • 除在 sizeof 的操作数中外,不使用以下任何实体:
  • 函数
  • 类对象
  • 指针
  • 引用
  • 赋值运算符
  • 自增运算符
  • 自减运算符
  • 函数调用运算符
  • 逗号运算符

其他常量表达式类别

其他表达式仅在 常量初始化 的上下文中被视为常量表达式。此类常量表达式必须是以下表达式之一:

  • 求值为 空指针值 的表达式
  • 求值为空成员指针值的表达式
  • 算术常量表达式
  • 地址常量表达式
  • 引用常量表达式
  • 完整对象类型的地址常量表达式,加上或减去整型常量表达式
  • 成员指针常量表达式

算术常量表达式 是满足整型常量表达式要求的表达式,但存在以下例外:

  • 可使用浮点数字面量而无需显式转换
  • 可应用向浮点类型的转换

地址常量表达式 是满足以下所有条件的指针类型表达式:

  • 显式使用取址运算符
  • 隐式使用指针类型的常量模板参数
  • 使用数组或函数类型的表达式
  • 表达式不调用任何函数
  • 表达式使用显式指针转换(除 dynamic_cast 外)及以下运算符,且不访问结果对象:
  • 下标运算符
  • 解引用运算符
  • 取址运算符
  • 成员访问运算符
  • 若使用下标运算符,则其任一操作数为整型常量表达式

引用常量表达式 是满足以下所有条件的引用类型表达式:

  • 引用指向静态存储期对象、引用类型的常量模板参数或函数。该引用不得指向非 POD 类类型的成员或基类
  • 表达式不调用任何函数
  • 表达式使用显式引用转换(除 dynamic_cast 外)及以下运算符,且不访问结果对象:
  • 下标运算符
  • 解引用运算符
  • 取址运算符
  • 成员访问运算符
  • 若使用下标运算符,则其任一操作数为整型常量表达式

成员指针常量表达式 是通过对限定标识符应用取址运算符(可选地前置显式成员指针转换)创建的成员指针类型表达式

(C++11 前)

下列表达式统称为 常量表达式

  • 具有静态存储期的对象的地址
  • 函数的地址
  • 空指针值
(C++11 起)
(C++14 前)

下列实体属于 常量表达式允许的结果 :

常量表达式 要么是一个指代常量表达式允许结果的 核心常量表达式 泛左值,要么是满足以下约束的纯右值核心常量表达式:

  • 若该值为类类型对象,其每个引用类型的非静态数据成员均指代常量表达式允许的结果实体
  • 若该值为 scalar type 对象,则其不具 indeterminate
  • 若该值为 pointer type ,则其应为下列值之一:
  • 具有静态存储期限对象的地址
  • 具有静态存储期限对象末尾后一位置的地址
  • 非立即 (since C++20) 函数的地址
  • null pointer value
  • 若该值为指向成员函数指针类型,则其不指代立即函数
(since C++20)
  • 若该值为类或数组类型对象,其每个子对象均满足上述值约束
(since C++14)
(until C++26)

常量表达式 要么是一个指代对象或非 立即函数的泛左值 核心常量表达式 ,要么是一个满足以下约束的纯右值核心常量表达式:

(始于 C++26)

在确定表达式是否为常量表达式时, copy elision 被假定为不会执行。

C++98 对常量表达式的定义完全包含在折叠框内。以下描述适用于 C++11 及后续 C++ 版本。

字面类型

以下类型统称为 literal types

(since C++17)
  • 满足以下条件之一的 聚合 联合类型:
  • 没有 变体成员
  • 至少有一个非易失性字面类型的变体成员。
  • 非联合聚合类型,且其每个 匿名联合 成员满足以下条件之一:
  • 没有变体成员。
  • 至少有一个非易失性字面类型的变体成员。
  • 具有至少一个非拷贝或移动构造函数的 constexpr 构造函数(模板)的类型

只有字面类型的对象才能在常量表达式中创建。

核心常量表达式

core constant expression 是指其求值过程 不会 涉及以下任一语言结构的表达式:

语言结构 版本 文献
this 指针,除非在作为表达式组成部分被求值的 constexpr 函数 中,或出现在隐式或显式类成员访问表达式中 N2235
控制流穿过具有静态或线程 存储期 且不 可用于常量表达式 块作用域变量 声明 (C++23 起) P2242R3
  1. a function call expression that calls a function (or a constructor) that is not declared constexpr
    constexpr int n = std::numeric_limits<int>::max(); // 正确:max() 是 constexpr 函数
    constexpr int m = std::time(nullptr); // 错误:std::time() 不是 constexpr 函数
  2. a function call to a constexpr function which is declared, but not defined
  3. a function call to a constexpr function/constructor template instantiation where the instantiation fails to satisfy constexpr 函数/构造函数 requirements.
  4. a function call to a constexpr virtual function, invoked on an object whose dynamic type is constexpr-unknown
  5. an expression that would exceed the implementation-defined limits
  6. an expression whose evaluation leads to any form of core language undefined 或错误的 (始于 C++26) behavior, except for any potential undefined behavior introduced by 标准属性 .
    constexpr double d1 = 2.0 / 1.0; // 正确
    constexpr double d2 = 2.0 / 0.0; // 错误:未定义
    constexpr int n = std::numeric_limits<int>::max() + 1; // 错误:溢出
    int x, y, z[30];
    constexpr auto e1 = &y - &x;        // 错误:未定义
    constexpr auto e2 = &z[20] - &z[3]; // 正确
    constexpr std::bitset<2> a; 
    constexpr bool b = a[2]; // 未定义行为,是否被检测到未作规定
  7. (C++17 前) a lambda 表达式
  8. an lvalue-to-rvalue 隐式转换 unless applied to...
    1. 类型为(可能带有 cv 限定符) std::nullptr_t 的泛左值
    2. 指定了 可在常量表达式中使用 的对象的非易失性字面类型的泛左值
      int main()
      {
          const std::size_t tabsize = 50;
          int tab[tabsize]; // OK: tabsize 是常量表达式
                            // 因为 tabsize 可在常量表达式中使用
                            // 因为它具有 const 限定的整型,且
                            // 其初始化器是常量初始化器
          std::size_t n = 50;
          const std::size_t sz = n;
          int tab2[sz]; // 错误:sz 不是常量表达式
                        // 因为 sz 不可在常量表达式中使用
                        // 因为其初始化器不是常量初始化器
      }
    3. 引用其生存期始于该表达式求值内的非易失性对象的非易失性字面类型的泛左值
  9. an lvalue-to-rvalue 隐式转换 or modification applied to a non-active member of a union or its subobject (even if it shares a common initial sequence with the active member)
  10. an lvalue-to-rvalue implicit conversion on an object 其值是不确定的
  11. an invocation of implicit copy/move constructor/assignment for a union whose active member is mutable (if any), with lifetime beginning outside the evaluation of this expression
  12. (直至 C++20) an assignment expression that would change the active member of a union
  13. conversion from 指向 void 的指针 to a pointer-to-object type T* 除非该指针持有空指针值或指向其类型与 similar T 的对象 (C++26 起)
  14. dynamic_cast 其操作数为引用动态类型为constexpr未知对象的glvalue (C++20 起)
  15. reinterpret_cast
  16. (直至 C++20) pseudo-destructor call
  17. (C++14 前) an increment or a decrement operator
  18. (自 C++14 起) modification of an object, unless the object has non-volatile literal type and its lifetime began within the evaluation of the expression
    constexpr int incr(int& n)
    {
        return ++n;
    }
    constexpr int g(int k)
    {
        constexpr int x = incr(k); // 错误:incr(k) 不是核心常量表达式
                                   // 因为 k 的生命周期开始于 incr(k) 表达式之外
        return x;
    }
    constexpr int h(int k)
    {
        int x = incr(k); // 正确:x 不需要用核心常量表达式初始化
                         //
        return x;
    }
    constexpr int y = h(1); // 正确:用值 2 初始化 y
                            // h(1) 是核心常量表达式,因为
                            // k 的生命周期开始于表达式 h(1) 内部
  19. (自 C++20 起) a destructor call or pseudo destructor call for an object whose lifetime did not begin within the evaluation of this expression
  20. a typeid expression applied to a glvalue of polymorphic type 且该泛左值所引用的对象具有常量表达式未知的动态类型 (C++20 起)
  21. a new expression ,除非满足以下条件之一: (C++20 起)
    • 所选的 allocation function 是可替换的全局分配函数,且分配的存储空间在此表达式求值期间被释放。
    (since C++20)
    • 所选的分配函数是具有分配类型 T 的非分配形式,且 placement 参数满足以下所有条件:
    • 它指向:
    • 一个类型与 T 相似的对象(若 T 不是数组类型),或
    • T 相似类型的对象的首元素(若 T 是数组类型)。
    • 它指向的存储空间其生存期在此表达式求值期间开始。
    (since C++26)
  22. a delete 表达式 ,除非它释放了在此表达式求值过程中分配的存储区域 (C++20 起)
  23. (自 C++20 起) Coroutines: an await表达式 or a yield表达式
  24. (自 C++20 起) a 三路比较 when the result is unspecified
  25. an equality or relational operator whose result is unspecified
  26. (C++14 前) an assignment or a compound assignment operator
  27. (C++26 前) throw 表达式
  28. (C++26 起) 异常对象的构造,除非该异常对象及其通过调用 std::current_exception std::rethrow_exception 创建的所有隐式副本都在该表达式求值期间被销毁
    constexpr void check(int i)
    {
        if (i < 0)
            throw i;
    }
    constexpr bool is_ok(int i)
    {
        try {
            check(i);
        } catch (...) {
            return false;
        }
        return true;
    }
    constexpr bool always_throw()
    {
        throw 12;
        return true;
    }
    static_assert(is_ok(5)); // 正确
    static_assert(!is_ok(-1)); // C++26 起正确
    static_assert(always_throw()); // 错误:未捕获异常
  29. asm 声明
  30. 调用 va_arg
  31. goto 语句
  32. dynamic_cast typeid 表达式 new 表达式 (C++26 起) 会抛出异常的情况 且异常类型定义不可达时 (C++26 起)
  33. lambda 表达式内对 this 或外部变量的引用,若该引用构成 ODR 使用
    void g()
    {
        const int n = 0;
        constexpr int j = *&n; // 正确:在 lambda 表达式外
        [=]
        {
            constexpr int i = n;   // 正确:'n' 未在此处被 ODR 使用且未被捕获
            constexpr int j = *&n; // 错误:'&n' 将构成对 'n' 的 ODR 使用
        };
    }

    注意:若 ODR 使用发生在对闭包的函数调用中,则不会引用 this 或外部变量,因为此时访问的是闭包的数据成员

    // 正确:'v' 和 'm' 被 ODR 使用但未在嵌套 lambda 的常量表达式中出现
    //
    auto monad = [](auto v){ return [=]{ return v; }; };
    auto bind = [](auto m){ return [=](auto fvm){ return fvm(m()); }; };
    // 允许捕获在常量表达式求值期间创建的自动对象
    static_assert(bind(monad(2))(monad)() == monad(2)());
    (C++17 起)

额外要求

即使表达式 E 未出现上述任何求值情形,若对 E 进行求值会导致 运行时未定义行为 ,则 E 是否为核心常量表达式由实现定义。

即使表达式 E 未计算上述任何内容,若对 E 进行求值将涉及以下任意情形,则 E 是否为核心常量表达式是未指定的:

  • 标准库中具有未定义行为的操作。
  • va_start 宏的调用。

为确定表达式是否为核心常量表达式,若 T 为字面类型,则忽略 std:: allocator < T > 成员函数体的求值过程。

为确定表达式是否为核心常量表达式,对 union 的平凡拷贝/移动构造函数或拷贝/移动赋值运算符的调用,其求值过程被视为拷贝/移动该union的活动成员(如果存在)。

为确定表达式是否为核心常量表达式,对命名 结构化绑定 bd 的标识符表达式求值时遵循以下语义:

  • bd 是引用被绑定对象的左值,其行为如同指代了虚构引用 ref
  • 否则,若 bd 命名数组元素,其行为等同于对 e [ i ] 求值,其中 e 是从结构化绑定声明初始化器初始化的变量名, i bd 所引用元素的下标。
  • 否则,若 bd 命名类成员,其行为等同于对 e. m 求值,其中 e 是从结构化绑定声明初始化器初始化的变量名, m bd 所引用的成员名。
(C++26 起)

在将表达式作为核心常量表达式求值期间,所有标识符表达式以及使用 * this 引用其生命周期始于表达式求值之外的对象或引用的情形,均被视为引用该对象或引用的特定实例,该实例及其所有子对象(包括所有联合成员)的生命周期涵盖整个常量求值过程。

  • 对于这样的对象 不可在 常量表达式中使用 (C++20 起) 的,该对象的动态类型为 constexpr-unknown
  • 对于这样的引用 不可在常量表达式中使用 (C++20 起) 的,该引用被视为绑定到被引用类型的一个未指定对象,该对象及其所有子对象的生存期涵盖整个常量求值过程,且其动态类型为 constexpr-unknown。

整型常量表达式

整型常量表达式 是指隐式转换为纯右值的整型或无作用域枚举类型表达式,其中转换后的表达式为核心常量表达式。

如果需要在期望整型常量表达式的地方使用类类型的表达式,该表达式将被 上下文隐式转换 为整型或无作用域枚举类型。

转换后的常量表达式

转换后的常量表达式 是指一个表达式被 隐式转换 为类型 T ,其中转换后的表达式是一个常量表达式,且隐式转换序列仅包含:

(自 C++17 起)

并且如果发生任何 引用绑定 ,它只能是 直接绑定

以下上下文需要转换后的常量表达式:

(自 C++14 起)
(自 C++26 起)

一个 类型为 bool 的上下文转换常量表达式 是指一个表达式, 经上下文转换为 bool ,其中被转换的表达式是常量表达式,且转换序列仅包含上述转换。

以下上下文需要类型为 bool 的上下文转换常量表达式:

(C++23 前)
(C++17 起)
(C++23 前)
(C++20 起)


组成实体

对象 obj 组成值 定义如下:

对象 obj 组成引用 包括以下引用:

  • obj 的任何具有引用类型的直接成员
  • obj 的任何直接子对象(不包括非活动联合成员)的组成引用

变量 var 组成值 组成引用 定义如下:

  • var 声明一个对象,则组成值和引用为该对象的组成值和引用。
  • var 声明一个引用,则组成引用为该引用。

对于变量 var 的任何组成引用 ref ,若 ref 绑定到临时对象或其子对象,且该临时对象的生存期已延长至 ref 的生存期,则该临时对象的组成值和引用也递归地成为 var 的组成值和引用。

可常量表达式表示的实体

具有静态存储期的对象在程序任意点均为 可常量表达式引用

具有自动存储期的对象 obj 在点 P 处为 可常量表达式引用 ,需满足:包含变量 var 的最小作用域与包含 P 的最小作用域为同一函数参数作用域,且该作用域不与 requires 表达式 的参数列表关联。其中 var 为对应 obj 完整对象的变量,或其生存期被 延长 至该变量生存期的变量。

对象或引用 x 在点 P 处为 可常量表达式表示 ,需满足以下所有条件:

  • 对于 x 的每个指向对象 obj 的组成值, obj P 处可常量表达式引用。
  • 对于 x 的每个指向超过对象 obj 的组成值, obj P 处可常量表达式引用。
  • 对于 x 的每个引用对象 obj 的组成引用, obj P 处可常量表达式引用。
(C++26 起)

常量初始化实体

当满足以下所有条件时,变量或临时对象 obj 被称为 常量初始化

  • 它要么具有初始化器,要么其类型为 常量默认可构造
  • 其初始化的 完整表达式 在需要常量表达式的上下文中是一个常量表达式,但如果 obj 是对象,该完整表达式也可以调用 obj 及其子对象的 constexpr 构造函数 ,即使这些对象属于非字面类类型。
(直至 C++26)

当满足以下所有条件时,变量 var 被称为 可常量初始化

  • 其初始化的 完整表达式 在需要常量表达式的上下文中是一个常量表达式,其中所有 合约断言 均使用“忽略”求值语义。
  • var 的初始化声明之后,由 var 声明的对象或引用是常量表达式可表示的。
  • 如果由 var 声明的对象或引用 x 具有静态或线程存储期,则 x 在紧随 var 初始化声明之后、且最内层作用域为命名空间作用域的位置是常量表达式可表示的。

可常量初始化的变量在以下情况下被称为 常量初始化 :它要么具有初始化器,要么其类型为 常量默认可构造

(自 C++26 起)

可在常量表达式中使用

一个变量是 潜在常量 的,如果它是 constexpr 变量 或者具有引用类型、非volatile的const限定整型或枚举类型。

常量初始化的潜在常量变量 var 在点 P 可用于常量表达式 的条件是:当 var 的初始化声明 D 可从 P 到达,且满足以下任一条件:

  • var 是一个 constexpr 变量。
  • var 未被初始化为 TU-local 值。
  • P D 位于同一翻译单元。

若对象或引用满足以下任一条件,则在点 P 处为 可在常量表达式中使用 的实体:

  • P 处可在常量表达式中使用的变量
  • 其生存期被延长至在 P 处可在常量表达式中使用的变量的非 volatile const 限定字面类型的临时对象
  • 模板参数对象
  • 字符串字面量 对象
  • 上述任意实体的不可变子对象
  • 上述任意实体的引用成员
(C++26 前)

若对象或引用满足以下任一条件,则在点 P 处为 可能可在常量表达式中使用 的实体:

  • P 处可在常量表达式中使用的变量
  • 其生存期被延长至在 P 处可在常量表达式中使用的变量的非 volatile const 限定字面类型的临时对象
  • 模板参数对象
  • 字符串字面量 对象
  • 上述任意实体的不可变子对象
  • 上述任意实体的引用成员

若对象或引用是在点 P 处可能可在常量表达式中使用,且在点 P 处为 constexpr 可表示的,则该对象或引用在点 P 处为 可在常量表达式中使用 的实体。

(C++26 起)

显式常量求值表达式

下列表达式(包括到目标类型的转换)是 明显常量求值 :

是否在明显常量求值上下文中进行评估可以通过 std::is_constant_evaluated if consteval (C++23 起) 来检测。

(自 C++20 起)

常量求值所需的函数与变量

下列表达式或转换是 可能被常量求值的

函数在以下情况下 需要用于常量求值 :如果它是 constexpr 函数,且 被可能进行常量求值的表达式 所命名。

变量在以下情况下 需要进行常量求值 :如果它是 constexpr 变量,或者是非 volatile const 限定的整型类型或引用类型,且 标识符表达式 所指向的内容可能被常量求值。

默认函数的定义以及 函数模板 特化的实例化 变量模板 特化 (C++14 起) 会在该函数 或变量 (C++14 起) 被需要用于常量求值时触发。

常量子表达式

常量子表达式 是指作为表达式 e 子表达式 进行求值时,不会阻止 e 成为 核心常量表达式 的表达式,其中 e 不能是以下任意表达式:

(C++20 起)

注释

功能测试宏 标准 功能
__cpp_constexpr_in_decltype 201711L (C++20)
(DR11)
常量求值所需 时生成函数和变量定义
__cpp_constexpr_dynamic_alloc 201907L (C++20) constexpr 函数中的动态存储期操作
__cpp_constexpr 202306L (C++26) constexpr void * 的转换:迈向constexpr类型擦除
202406L (C++26) constexpr 布局 new new [ ]
__cpp_constexpr_exceptions 202411L (C++26) constexpr 异常: [1] [2]

示例

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 适用范围 发布时行为 正确行为
CWG 94 C++98 算术常量表达式不能
涉及变量和静态数据成员
现在可以
CWG 366 C++98 涉及字符串字面量的表达式
可能被视为整型常量表达式
实际并非如此
CWG 457 C++98 涉及 volatile 变量的表达式
可能是整型常量表达式
实际并非如此
CWG 1293 C++11 尚不明确字符串字面量
是否可用于常量表达式
可以用于常量表达式
CWG 1311 C++11 volatile 泛左值可在常量表达式中使用 已禁止
CWG 1312 C++11 reinterpret_cast 在常量表达式中被禁止使用,
但通过转换为 void * 及从该类型转换仍可实现相同效果
禁止从 cv void * 类型
到对象指针类型的转换
CWG 1313 C++11 允许未定义行为;
禁止所有指针减法运算
禁止未定义行为;同数组
指针减法运算允许
CWG 1405 C++11 对于可在常量表达式中使用的对象,
其可变子对象也可使用
它们不可使用
CWG 1454 C++11 不允许通过引用将常量传递给
constexpr 函数
允许
CWG 1455 C++11 转换后的常量表达式只能是纯右值 可以是左值
CWG 1456 C++11 地址常量表达式不能
指向数组末尾后一位地址
允许
CWG 1535 C++11 当操作数为多态类类型时,
typeid 表达式即使不涉及运行时检查
也不被视为核心常量表达式
操作数约束被限定为
多态类类型的泛左值
CWG 1581 C++11 常量求值所需的函数
未被要求定义或实例化
已要求
CWG 1613 C++11 核心常量表达式可计算 lambda 表达式内
任何被 odr 使用的引用
某些引用可能
无法被计算
CWG 1694 C++11 将临时对象的值绑定到静态存储期引用是常量表达式 它不是常量表达式
CWG 1872 C++11 核心常量表达式可能调用 constexpr 函数模板
实例化,但这些实例化不满足 constexpr 函数要求
此类实例化
不可被调用
CWG 1952 C++11 标准库未定义行为
需被诊断的要求
是否进行诊断
未作规定
CWG 2022 C++98 常量表达式的确定可能
取决于是否执行了复制消除
假定始终执行复制消除
CWG 2126 C++11 常量初始化的生命周期延长临时对象(const限定字面类型)
无法在常量表达式中使用
可用
CWG 2129 C++11 整数字面值不是常量表达式 它们是
CWG 2167 C++11 求值中的局部非成员引用
导致该求值无法成为常量表达式
允许非成员
引用
CWG 2278 C++98 CWG 问题 2022 的解决方案不可实现 假定复制消除
永远不会执行
CWG 2299 C++14 宏在 <cstdarg>
中是否可用于常量求值尚不明确
va_arg 被禁止,
va_start 未作规定
CWG 2400 C++11 对不可用于常量表达式且其生存期始于包含该调用的表达式之外的
对象调用 constexpr 虚函数可能构成常量表达式
不应被视为
常量表达式
CWG 2490 C++20 (伪)析构函数调用在常量求值中
缺乏限制
已添加限制
CWG 2552 C++23 在计算核心常量表达式时,控制流
不能通过非块变量的声明
允许通过
CWG 2558 C++11 不确定值可能构成常量表达式 不构成常量表达式
CWG 2647 C++20 volatile限定类型的变量可能具有常量性 实际并不具备
CWG 2763 C++11 在常量求值期间未要求检测 [[ noreturn ]] 的违规使用 要求检测
CWG 2851 C++11 转换后的常量表达式
不允许浮点转换
允许非窄化
浮点转换
CWG 2907 C++11 核心常量表达式无法对
std::nullptr_t 左值实施
左值到右值转换
允许实施此类
转换
CWG 2909 C++20 没有初始化器的变量仅当其默认初始化
会导致某些初始化操作被执行时
才能进行常量初始化
仅当其类型为
常量默认可初始化时
才能进行常量初始化
CWG 2924 C++11
C++23
未明确说明违反
[[ noreturn ]] (C++11) 或
[[ assume ]] (C++23) 约束的表达式
是否为核心常量表达式
由实现定义
P2280R4 C++11 包含引用其生命周期开始于此求值之外的
对象或引用的标识符表达式
* this 的表达式求值不是常量表达式
可以成为
常量表达式

另请参阅

constexpr 说明符 (C++11) 指定变量或函数的值可在编译时计算
(C++11) (C++17 中弃用) (C++20 中移除)
检查类型是否为字面类型
(类模板)
C 文档 关于 常量表达式