为什么在块作用域中`constexpr const int &a = 1;`失败了?

7

N4527 7.1.5[dcl.constexpr]p9

在对象声明中使用的constexpr指定符将该对象声明为const。这样的对象应具有字面类型并且应进行初始化。如果它通过构造函数调用进行初始化,则该调用应为常量表达式(5.20)。否则,或者如果在引用声明中使用constexpr指定符,则出现在其初始化器中的每个完整表达式都应是常量表达式。

5.20[expr.const]p5

常量表达式是指值引用了常量表达式所允许的实体的glvalue核心常量表达式,或者是一个prvalue核心常量表达式,其值是一个对象,在该对象及其子对象上:

— 每个非静态数据成员的引用类型都引用了一个被允许的常量表达式实体,并且

— 如果对象或子对象是指针类型,则它包含具有静态存储期限的对象的地址,这种对象不是临时对象,或者是满足上述约束条件的临时对象的值,或者是函数的地址或空指针值。

如果一个实体是常量表达式的允许结果,则它是具有静态存储期限的对象,该对象既不是临时对象,也是满足上述约束条件的临时对象,或者它是一个函数。

void foo(){
    constexpr const int &a = 1;//error
    constexpr static const int &b = 1;//ok in gcc 5.1.0 and clang 3.8.0

}

问题:为什么在块作用域中constexpr const int &a = 1;会失败?


@Shafik Yaghmour N4140没有5.20 - stackcpp
1个回答

6

这在 cwg缺陷报告2005:不正确的constexpr引用初始化要求 中有所涉及,其中指出(强调我的):

Consider an example like:

  constexpr int f() { return 5; } // function must be constexpr
  constexpr int && q = f();       // but result is not constant
  constexpr int const & r = 2;    // temporary is still not constant
  int main() {
    q = 11;                       // OK
    const_cast< int & >( r ) = 3; // OK (temporary object is not ROMable)

    constexpr int && z = 7;       // Error? Temporary does not have static storage duration?
  }

A constexpr reference must be initialized by a constant expression (7.1.5 [dcl.constexpr] paragraph 9), yet it may refer to a modifiable temporary object. Such a temporary is guaranteed static initialization, but it's not ROMable.

A non-const constexpr reference initialized with an lvalue expression is useful, because it indicates that the underlying storage of the reference may be statically initialized, or that no underlying storage is required at all.

When the initializer is a temporary, finding its address is trivial. There is no reason to declare any intent the computation of its address. On the other hand, an initial value is provided, and that is also required to be a constant expression, although it's never treated as a constant.

The situation is worse for local constexpr references. The initializer generates a temporary when the declaration is executed. The temporary is a locally scoped, unique object. This renders constexpr meaningless, because although the address computation is trivial, it still must be done dynamically.

C++11 constexpr references required initialization by reference constant expressions, which had to “designate an object with static storage duration or a function” (C++11 5.20 [expr.const] paragraph 3). A temporary with automatic storage duration granted by the reference fails this requirement.

C++14 removes reference constant expressions and the static storage requirement, rendering the program well-defined with an apparently defeated constexpr specifier. (GCC and Clang currently provide the C++11 diagnosis.)

Suggested resolution: a temporary bound to a constexpr reference should itself be constexpr, implying const-qualified type. Forbid binding a constexpr reference to a temporary unless both have static storage duration. (In local scope, the static specifier fixes the issue nicely.)

回应是,这已经被第5.20段第4款禁止:

这个问题已经在5.20 [expr.const]第4款中得到了覆盖,其中包括分析中的转换和临时变量。


据我所知,在这种情况下适用的句子应该是“引用绑定(如果有)直接绑定”。 - Shafik Yaghmour
其初始化器必须是一个常量表达式,并且 5.20 [expr.const] p4 是“转换后的常量表达式”,为何在此处应用 p4? - stackcpp
@dyp,这是我最初的想法,但他们使用的是5.20,这是该段落移动后的编号,或者至少据我所知是这样。 - Shafik Yaghmour
好的,引用中说“包括转换和临时变量”,我认为p5没有讨论或包含它(因此我已经删除了早期的评论)。P4中的引用绑定非常令人困惑,我不太明白它指的是什么(哎呀,双关语不是故意的)。如果它确实是指对constexpr T&引用变量进行初始化,那么为什么整个段落都在谈论转换后的常量表达式?但否则,它似乎很合适。 - dyp
@dyp 同意,不太确定,我一直在试图拼凑它,但是并不明显。第4段的项目符号版本似乎来自这里:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4268.html,当时它还是第3段,并包括“引用常量表达式是指定具有静态存储期或函数的对象的左值核心常量表达式”。所以他们当时认为它涵盖了该情况。 - Shafik Yaghmour
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接