使用 constinit 变量初始化一个 constexpr 变量

Using constinit variable to initialize a constexpr variable

看看这个小例子:

constinit int a = 0;
constexpr int b = a;

clang 不编译它 (godbolt):

2:15: error: constexpr variable 'b' must be initialized by a constant expression

这是正确的诊断吗?

如果是,为什么标准不允许这样做?我知道,a 的值可能会在 运行 期间(甚至在动态初始化期间)发生变化,但在常量初​​始化时,它的值是已知的,因此它可以用于初始化 b.

这是正确的诊断吗?

我会说是的。根据cppreference:

constinit - specifies that a variable must have static initialization, i.e. zero initialization and constant initialization, otherwise the program is ill-formed.

静态(常量)初始化和常量表达式是不同的概念,常量表达式可以用于常量初始化但反之则不行constinit 不应与 const 混淆。这意味着初始化(仅)是常量。

但是,constinit const 可以用在 constexpr 中,它们应该是相同的。

反例:

constinit int a = 0;

struct X{
    X() {
        a = 4;
    }
};

X x{};

constexpr int b = a;

b 应该是什么? 关键是 a 可以在 bseen.

之前以非常量方式更改

constexpr毫无例外地结合了constinitconst

constinit 使用编译时常量表达式强制初始化,并且在静态初始化期间,不允许动态初始化。它不会以任何方式改变变量本身。

const 禁止更改变量,但可以被 mutable 成员削弱。

两者一起使其成为编译时常量表达式。

综上所述,是的,诊断是正确的。

是的,诊断是正确的。 constexpr 变量必须用常量表达式初始化,a 不是常量表达式(它是可变变量)。

constinit (P1143) 的目的是在初始化 不是常量的情况下强制变量声明格式错误。它不会改变变量本身的任何东西,比如它的类型或任何东西(以 constexpr 隐式 const 的方式)。愚蠢的例子:

struct T {
    int i;
    constexpr T(int i) : i(i) { }
    T(char c) : i(c) { }
};

constinit T c(42); // ok
constinit T d('X'); // ill-formed

这就是 constinit 的全部内容,唯一真正的规则是 [dcl.constinit]/2:

If a variable declared with the constinit specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed. [ Note: The constinit specifier ensures that the variable is initialized during static initialization ([basic.start.static]). — end note ]

constinit中的const只指初始化,不是变量,不是任何类型。请注意,它也不会 更改 执行的初始化类型,它只是诊断是否执行了错误的类型。

在:

constinit int a = 0;
constexpr int b = a;

0是常量表达式,所以a的初始化是合式的。一旦我们越过那个,说明符就不会做任何事情。相当于:

int a = 0; // same behavior, a undergoes constant initialization
constexpr int b = a;

这是完全错误的。


but at constant-initialization, its value is known, so it could be used to initialize b.

当然,现在。怎么样:

constinit int a = 0;
cin >> a;
constexpr int b = a;

那显然不会飞。允许这样做需要扩展什么是常量表达式(在我看来这已经是标准中最复杂的规则)以允许非常量变量但仅在初始化后立即出现?复杂性似乎不值得,因为你总是可以写:

constexpr int initializer = 0;
constinit int a = initializer;
constexpr int b = initializer;