在常量表达式上下文中,在定义之前嵌套使用`constexpr`函数调用

4
据我从这个答案中了解到,如果函数尚未声明,constexpr函数的结果不是常量表达式。令我惊讶的是以下代码片段:
constexpr int f();

constexpr int g() {
    return f();
}

constexpr int f() {
    return 42;
}

int main() {
    constexpr int i = g();
    return i;
}

这段代码可以编译并正常工作。将f的定义移到main函数之后就会触发错误:error: 'constexpr int f()' used before its definition,这是我预料中的。

我猜测它可行是因为在调用g之前已经定义了f,因此两次调用都是常量表达式。

为什么f()g()看起来是常量表达式,即使f在被g调用时还没有定义?标准如何描述这一点?

我已在Coliru的GCC 6.1.0和Clang 3.8.0上进行了测试。


1
5.20/(2.3): “unless ... invocation of an undefined constexpr function” ? - Kerrek SB
我认为我们应该强调的是constexpr函数只有在被odr-used时才需要定义。因此,即使g()中没有f()的定义,编译器也可以通过简单的声明猜测出g()的主体,但是要实际调用g(),您需要f()的定义,因为调用函数被视为odr-use。所以,是的,给定的答案是有效的,但我认为这就是OP感到困惑的地方。 - KABoissonneault
3
请参阅CWG2166 - T.C.
@T.C. 看起来对我来说就是一个答案 :) - Quentin
2个回答

0

在使用constexpr之前并不需要定义它。但是,在定义之前调用它的结果不是constexpr。因此,编译器会正确地抱怨,因为您试图使用非常量表达式初始化constexpr变量。

§5.20/p2 常量表达式 [expr.const] (强调我的) :

条件表达式e是核心常量表达式,除非按照抽象机器(1.9)的规则评估e将评估以下表达式之一:

...

(2.3) — 未定义的constexpr函数的调用或未定义的constexpr构造函数;


抱歉,我觉得我的问题没有表达清楚。我的意思是为什么调用实际上是一个常量表达式,即使在调用它的时候f没有被定义? - Quentin
你的引用可以被解读为允许或者禁止 OP 的代码,不清楚你是如何理解它的,也无法确定这种解读(无论哪种)是否正确。 - user743382
1
鉴于您的编辑:这仍然留下了一个重要问题未解答。 [dcl.constexpr]p5(“对于非模板,非默认constexpr函数[...],如果不存在任何参数值使得函数的调用[...]可以成为核心常量表达式(5.19)的评估子表达式,则程序无效;不需要诊断。”)是否允许编译器在定义g之后立即发出错误消息?在定义f之前,没有参数值可以使得g()的评估结果成为常量表达式。 - user743382
@hvd 嗯...我认为这是有争议的,因为“不需要诊断”。 - 101010
我认为 @hvd 已经理解了我想表达的意思 - 当编译 g 时,f() 不能是常量表达式,那么为什么“决定”要延迟到实际调用 g() 时才做出? - Quentin
@Quentin 在这种情况下,该程序是不规范的,但不需要进行诊断。也就是说,编译器无需发出诊断/错误提示。因此,当您尝试使用非常量表达式初始化 constexpr 时,稍后会出现错误。 - 101010

0

正如T.C.在他的评论中提到的,这取决于一个缺陷报告

根据5.20 [expr.const]第2.3条款,表达式是常量表达式,除非(其他原因之一)它会评估

  • 未定义的constexpr函数或未定义的constexpr构造函数的调用;

这并没有解决constexpr函数必须在何时定义的问题。为了允许相互递归的constexpr函数,意图是函数必须在最终导致调用的最外层评估之前定义,但这并没有明确说明。

这使得例子是良好形式化的,并且只要在调用g之前定义f,它应该按预期工作。


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