问题 gcc是否将非常量表达式函数的内置函数视为常量表达式


请参阅更新以获得更好的问题示例。原始代码有各种各样的问题,使图片混乱

这个问题 为什么我可以在constexpr函数中调用非constexpr函数? 提出以下代码

#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 允许它(看到它)。

但是,如果我们使用 -fno-builtin 旗 gcc 产生错误(看到它):

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

所以 seems 那 gcc 正在考虑其内置版本 printf 成为一个不变的表达。 gcc  文件内置在这里 但是没有记录这种情况,其中非constexpr函数的内置可以被认为是常量表达式。

如果确实如此:

  • 是否允许编译器执行此操作?
  • 如果他们被允许,他们不必记录它是否符合要求?
  • 这可以被视为一个扩展,如果是这样,似乎这需要一个警告作为 C ++草案标准 部分 1.4  实施合规性 段 8 说(强调我的):

符合条件的实现可能具有扩展(包括其他库函数),前提是它们不会改变任何格式良好的程序的行为。 需要实现来诊断使用根据本国际标准格式不正确的扩展的程序。但是,这样做后,他们可以编译和执行这些程序。

更新

正如凯西所指出的那样,原始问题中有一些事情会使它成为一个糟糕的例子。一个简单的例子就是使用 性病::战俘 这不是constexpr功能:

#include <cmath>
#include <cstdio>

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

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

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

编译和构建时没有警告或错误(看到它)但添加 -fno-builtin 使它产生错误(看到它)。注意: 为什么数学函数不是c ++ 11中的constexpr

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

9637
2018-03-04 20:34


起源

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


答案:


是, gcc 正在考虑一些 内置功能 如 constexpr 即使标准没有明确标记它们。我们可以找到专门针对数学函数的讨论 cmath 在里面 gcc 错误报告 [C ++ 0x] sinh vs asinh vs constexpr 其中说:

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

这指的是 LWG 2013年问题 其原先提出的决议是将以下内容添加到该部分 17.6.5.6  [constexpr.functions] (强调我的前进):

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

但是在C ++ 11之后,分辨率被颠倒了,最终的分辨率结束了:

[...]实施 不得申报 任何标准库函数   签名为constexpr,除非是明确的签名   需要。[..]

所以这是目前(在C ++中14一个明确不符合的扩展 并且据我所知,这在C ++ 11中是不符合的,因为它改变了可观察的行为,因此不允许通过 as-if规则

Jonathan Wakely指出了一个 libstdc++ 邮件列表讨论: 重新访问PR libstdc ++ / 49813:constexpr on functions(和builtins)  由于上面列出的问题,讨论了重新打开上述错误报告的地方:

我相信我们应该根据实际的分辨率重新打开这个bug   LWG 2013(禁止添加constexpr)。

FE不应该将内含者视为严格一致的constexpr   模式。

我们应该从<cmath>中完全删除_GLIBCXX_CONSTEXPR或   以__STRICT_ANSI__为条件。


6
2018-01-12 16:51



很好的总结。也可以看看 gcc.gnu.org/ml/libstdc++/2014-09/msg00004.html - Jonathan Wakely


海湾合作委员会  考虑 f() 成为一个不变的表达。查看诊断信息 您链接的第一个示例程序

main.cpp:在函数'int main()'中:
main.cpp:10:19:警告:ISO C ++禁止变长数组'a'[-Wvla]
         char a [f()];
                   ^

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

如果您将程序更改为强制 f() 变成一个常量表达式:

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

GCC报告错误

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

4
2018-03-05 02:11



这个更简单 用例子 strlen,我必须再看一下另一个例子。 - Shafik Yaghmour
@ShafikYaghmour strlen 没有副作用。很可能 constexpr。 考虑到这个编译,我会说它出于各种目的 是  constexpr 对于gcc + args的特殊风味。有趣的是,C ++ 1y(好吧,N3936无论如何)17.6.5.6 [constexpr.functions] / 1可能禁止这种行为:“这个标准明确要求某些标准库函数是constexpr(7.1.5)。实现不应声明任何标准库函数签名为constexpr,除非明确要求它。“ - Casey
如果 你使用-fno-builtin 它会产生一个错误: error: call to non-constexpr function 'size_t strlen(const char*)' - Shafik Yaghmour
@ShafikYaghmour是的,我知道 - 因此我的声明“对于gcc + args的特殊风味”。 - Casey
+1原始示例有太多事情发生,所以我更新了我的问题并添加了一个更简单的示例 std::pow 因为我们知道它不可能是一个constexpr。我们也知道 gcc 这里正在使用内置函数在编译时生成一个常量。我仍然不清楚这是有效的。 - Shafik Yaghmour