如果表达式的求值需要对引用求值,为什么表达式不是 "core constant expression"?

Why isn't an expression a "core constant expression" if its evaluation requires the evaluation of a reference?

(这是 问题的后续。)

所以我想专门问一个问题,以了解我收到的 中引用的标准语,我的确切问题在标题中。

老实说,甚至 cppreference 我明白这是什么原因 为什么 标准这么说。

但是,这是最小的示例:

#include <array>
int main() {
    auto arr = std::array<int,3>{{1,2,3}};
    constexpr auto size1 = arr.size(); // OK
    auto const& array = arr;
    constexpr auto size2 = array.size(); // does not compile
}

没有编译错误(消息错误与 -std=11/14/17/2a 相同,因此两者的标记极端)

$ g++ -std=c++17 deleteme.cpp && ./a.out 
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:6:39: error: the value of ‘array’ is not usable in a constant expression
    6 |     constexpr auto size2 = array.size(); // does not compile
      |                                       ^
deleteme.cpp:5:17: note: ‘array’ was not initialized with a constant expression
    5 |     auto const& array = arr;
      |                 ^~~~~

但是如果我们删除 &.

确实 编译

另一方面,如果我只依赖上面写着 ‘array’ was not initialized with a constant expression 的注释,我会假设以下编译

#include <array>
int main() {
    constexpr auto arr = std::array<int,3>{{1,2,3}};
    constexpr auto size1 = arr.size(); // OK
    constexpr auto& array = arr;
    constexpr auto size2 = array.size(); // does not compile
}

但它没有,编译器说(消息错误与 -std=11/14/17/2a 相同)

$ g++ -std=c++17 deleteme.cpp && ./a.out 
deleteme.cpp: In function ‘int main()’:
deleteme.cpp:5:29: error: ‘arr’ is not a constant expression
    5 |     constexpr auto& array = arr;
      |                             ^~~

这基本上意味着 arr 不是“常量表达式”,即使它是 constexpr,这至少在我看来是一个非常糟糕的措辞。

我相信这是因为 constant-expression rules permit us to use the address-of operator, as hinted at in . For example, this is legal:

void f() {
    int i, j;
    constexpr bool b = &i == &j; // OK, b := false since i and j are distinct objects
}

允许在常量表达式中使用引用似乎无害:

void g() {
    int i, j;
    int& r = j;
    constexpr bool b = &i == &r; // OK, surely?
}

但是我们可以使引用的指称依赖于非常量变量,因此将非常量值偷偷带入常量求值中:

void h(bool a) {
    int i, j;
    int& r = a ? i : j;
    constexpr bool b = &i == &r; // oops, b := a
}

应该可以放宽the prohibition on evaluating references禁止获取(或使用?)参考地址,但这肯定需要一些努力确保新语言达到预期效果。

附录:论文 P2280R1 "Using unknown references in constant expressions" 旨在打击使您的原始示例格式错误的标准语。它(还)没有详细说明允许的内容,但似乎不允许形成或比较指向“未知”对象的指针,这些对象是 constexpr 上下文外部引用的引用对象,因此 g()会继续无效,更何况h().

变量声明中使用的

constexpr说明符要求初始化的完整表达式为常量表达式,其规则为:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints.

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

核心常量表达式应满足这些规则:

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 id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
  • it is initialized with a constant expression or.
  • its lifetime began within the evaluation of e;

在您的示例中,由于 arr 没有 静态存储持续时间 ,因此 id-expression array 既不会被常量表达式初始化,也不会它的生命周期始于 size2 的初始化评估。因此初始化不是常量表达式,程序格式错误。