[expr.const] 部分中的短语 "preceding initialization" 是什么意思

what is the meaning of the phrase "preceding initialization" in section [expr.const]

constexpr int func(int const& rf){
    return rf;
}
int main(){
   constexpr int value = func(0);
}

考虑上面的代码,变量value需要用一个常量表达式初始化,即func(0),首先应该是一个核心常量表达式。判断表达式func(0)是否为核心常量表达式,将应用如下规则,即:
expr.const#2.7

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

an lvalue-to-rvalue conversion unless it is applied to

[...], or

(2.7.4) a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

尽管 lvalue-to-rvalue conversion 应用于 rf 并且这种转换满足项目符号 (2.7.4),但是,请看下一段,即:
expr.const#2.11

an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either

  • (2.11.1) it is initialized with a constant expression or,
  • (2.11.2) its lifetime began within the evaluation of e;

我不知道 preceding initialization 这句话究竟是什么意思?这是否意味着一个变量应该在使用它之前被初始化,或者它意味着在一个变量的声明中应该有一个初始化器。无论如何,在将 lvalue-to-rvalue conversion 应用于 glvalue rf 之前,应该评估 glvalue rf 以确定对象的身份,这是由以下规则决定的:

A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.

也就是说不仅要遵守[expr.const#2.7],还要遵守[expr.const#2.11]

因为id-expression rf是引用类型。因此,为了使表达式 func(0) 成为核心常量表达式,id-expression rf 必须具有 preceding initialization,并且至少满足以下之一项目符号 (2.11.1) 和 (2.11.2)。在我的示例中,项目符号 (2.11.2) 遵循 rf 并且编译器同意 func(0) 是常量表达式。结果是 here,结果似乎证明 preceding initialization 意味着 be initialized 而不是由于参数声明在第一个示例中没有初始化程序而具有初始化程序。

为了验证这样的想法,我测试了下面的代码:

constexpr int func(int const& rf){
    constexpr int const& v = rf;  
    return 0;
}
int main(){
   static int const data = 0;
   constexpr int value = func(data);
}

编译器的outcomes指出rf不是常量表达式。我对这个结果感到困惑。根据以上推测。 rf有前置初始化,(2.11.1)是服从的,因为data是常量表达式,即使不满足(2.11.2)

所以,我想知道短语 preceding initialization 究竟是什么意思?如果这意味着变量的声明具有初始值设定项,那么第一个示例中的表达式 func(0) 怎么可能是常量表达式?

The outcome seems to evidence that "preceding initialization" means "be initialized" rather than have an initializer due to the parameter declaration does not have an initializer in the first example.

它确实意味着“被初始化”,但更重要的是关于先前初始化的可见性在被评估的表达式的上下文中[=37] =].在您的示例中,在评估 func(0) 的上下文中,编译器具有使用 0 查看 rf 初始化的上下文。但是,在仅计算 func 中的表达式 rf 的上下文中,它看不到 rf 的初始化。分析是局部的,因为它不会分析每个 call-site。这导致表达式 rf 本身在 func not 中是常量表达式,而 func(0) 是常量表达式。

如果你改为这样写:

constexpr int func(int const& rf) {
  /* constexpr */ int const& v = rf;
  return v;
}

int main() {
  static int const data = 0;
  constexpr int value = func(data);
}

这又没问题,因为在 func(data) 的上下文中,rf 有一个带有常量表达式 data 的前置初始化,而 v 有一个带有常量表达式的前置初始化rf 不是 常量表达式,但它的生命周期开始于 func(data).

的求值范围内

这是CWG2186:

2186. Unclear point that “preceding initialization” must precede

Similar to the concern of issue 2166, the requirement of 8.20 [expr.const] bullet 2.7.1 for

— a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or

does not specify the point at which the determination of “preceding initialization” is made: is it at the point at which the reference to the variable appears lexically, or is it the point at which the outermost constant evaluation occurs? There is implementation divergence on this point.

但意思应该是“词法”,因为ODR。