【问题标题】:Is gcc considering builtins of non-constant expression functions to be constant expressionsgcc 是否将非常量表达式函数的内置函数视为常量表达式
【发布时间】:2014-03-04 20:34:04
【问题描述】:

请查看更新以获得更好的问题示例。原始代码混合了一些混淆图片的问题

这个问题Why can I call a non-constexpr function inside a constexpr function?提供了以下代码

#include <stdio.h>

constexpr int f()
{
    return printf("a side effect!\n");
}

int main()
{
    char a[f()];
    printf("%zd\n", sizeof a);
}

我的回答是格式错误,但gcc 4.8.2 允许它(see it live)。

但是,如果我们使用-fno-builtin 标志gcc 会产生错误(see it live):

error: call to non-constexpr function 'int printf(const char*, ...)'
     return printf("a side effect!\n");
                                     ^

所以seems gcc 将其内置版本的printf 视为常量表达式。 gcc documents builtins here 但没有记录这种情况,即非 constexpr 函数的内置函数可以被视为常量表达式。

如果确实如此:

  • 允许编译器这样做吗?
  • 如果他们被允许,他们是否不必记录它以符合要求?
  • 这是否可以被视为扩展,如果是这样,这似乎需要一个警告,因为 C++ draft standard 部分 1.4 实施合规性 段落 8 说( 强调我的):

只要不改变任何格式良好的程序的行为,符合标准的实现可能具有扩展(包括额外的库函数)。 根据本国际标准,诊断使用此类格式错误的扩展程序的程序需要实现。然而,这样做之后,他们就可以编译和执行这样的程序了。

更新

正如凯西指出的那样,原始问题中发生了一些事情,使其成为一个糟糕的例子。一个简单的例子是使用不是 constexpr 函数的std::pow

#include <cmath>
#include <cstdio>

constexpr double f()
{
    return std::pow( 2.0, 2.0 ) ;
}

int main()
{
    constexpr double x = f() ;

    printf( "%f\n", x ) ;
}

编译和构建没有警告或错误 (see it live),但添加 -fno-builtin 会产生错误 (see it live)。注:why math functions are not constexpr in C++11:

error: call to non-constexpr function 'double pow(double, double)'
     return std::pow( 2.0, 2.0 ) ;
                               ^

【问题讨论】:

  • 你认为 gcc 到底违反了什么?是不是 [dcl.constexpr]/5 “对于 constexpr 函数,如果不存在函数参数值使得函数调用替换会产生常量表达式 (5.19),则程序格式错误;不需要诊断。” ?
  • 注意:我不太明白为什么当 gcc 拒绝它时你说“gcc 4.8.2 允许它”,调用f()不是一个常量表达式。然而,它不拒绝的是函数 f 本身的定义,AFAIK 不需要诊断。
  • 我不相信它会将内置函数视为常量表达式。您没有提供证明这一点的示例(并且调用f(),导致调用printf("a side effect!\n")被视为常量表达式)。
  • [继续..] 当切换到-fno-builtin 时,它诊断f 本身的格式错误。现在的问题是:“gcc 使用不同的开关显示这种不同的行为是否符合要求?”还是“gcc 接受非常量表达式作为数组绑定是否符合要求?”还是“当拒绝f时,gcc是否符合?”
  • 嗯,这个例子是更好的 IMO。 f 仍然格式不正确,不需要诊断,但 x = f() 格式不正确我认为需要进行诊断。接受程序是一种扩展,不产生任何关于它的消息是不合格的。

标签: c++ gcc c++11 language-lawyer constant-expression


【解决方案1】:

是的,gcc 正在考虑将一些 builtin functions 视为 constexpr,即使标准并未明确将它们标记为此类。我们可以在gcc 错误报告[C++0x] sinh vs asinh vs constexpr 中找到专门与cmath 中的数学函数相关的讨论,其中说:

LWG 2013 似乎确实允许 GCC 将这些函数视为 constexpr。 所以,固定为 4.7

指的是LWG issue 2013,其最初提议的解决方案是将以下内容添加到17.6.5.6 部分[constexpr.functions]强调我的未来):

[...]此外,实现可以声明任何函数 constexpr 如果该函数的定义满足必要 约束[...]

但在 C++11 之后,分辨率反转,最终的分辨率为:

[...]实现不应声明任何标准库函数 作为 constexpr 的签名,但明确表示的除外 需要。[..]

所以这是当前(在 C++14 中a explicitly non-conforming extension,据我所知,这在 C++11 中是不合格的,因为它会改变可观察的行为,因此不会通过as-if 规则允许。

Jonathan Wakely 指出了 libstdc++ 邮件列表讨论:PR libstdc++/49813 revisited: constexpr on functions (and builtins),由于上述问题,讨论了重新打开上述错误报告:

我认为我们应该根据实际分辨率重新打开错误 LWG 2013(禁止添加 constexpr)。

FE 不应将内置函数视为严格遵守的 constexpr 模式。

我们应该从 中完全删除 _GLIBCXX_CONSTEXPR 或者 使其以 __STRICT_ANSI__ 为条件。

【讨论】:

    【解决方案2】:

    GCC 确实f() 视为常量表达式。查看the first sample program you linked 的诊断:

    main.cpp:在函数“int main()”中:
    main.cpp:10:19:警告:ISO C++ 禁止可变长度数组“a”[-Wvla]
             字符 a[f()];
                       ^
    

    编译器不认为f() 是一个常量表达式,程序实际上是在使用允许可变长度数组的 GCC 扩展 - 具有非常量大小的数组。

    如果改程序强制f()变成常量表达式:

    int main() {
        constexpr int size = f();
        char a[size];
        printf("%zd\n", sizeof a);
    }
    

    GCC does report an error:

    main.cpp:在函数“int main()”中: main.cpp:10:32:在 'f()' 的 constexpr 扩展中 main.cpp:5:41: 错误:'printf(((const char*)"a side effect!\012"))' 不是常量表达式 return printf("副作用!\n"); ^

    【讨论】:

    • this is a simplerstrlen 的例子,我得再看看另一个例子。
    • @ShafikYaghmour strlen 没有副作用。很可能是constexprConsidering that this compiles,我想说的是 is constexpr 用于 gcc+args 的特殊风格。有趣的是,C++1y(好吧,无论如何都是 N3936)17.6.5.6 [constexpr.functions]/1 可能禁止这种行为:“该标准明确要求某些标准库函数是 constexpr (7.1.5)。实现不应声明任何标准库函数签名为 constexpr,除非明确要求。"
    • 如果you use -fno-builtin 会产生错误:error: call to non-constexpr function 'size_t strlen(const char*)'
    • @ShafikYaghmour 是的,我知道 - 因此我的声明是“针对 gcc + args 的那种特殊风味”。
    • +1 原始示例有太多事情要做,所以我更新了我的问题并使用std::pow 添加了一个更简单的示例,因为我们知道它不能是 constexpr。我们也知道gcc 在这里所做的是使用内置函数在编译时生成一个常量。我仍然不清楚这是否有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-30
    • 1970-01-01
    • 2017-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多