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

Is gcc considering builtins of non-constant expression functions to be constant expressions

本文关键字:常量 表达式 内置 函数 非常 是否 gcc      更新时间:2023-10-16

请参阅更新以获取更好的问题示例。原始代码混合了使图片混乱的问题:

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

#include <stdio.h>
constexpr int f()
{
return printf("a side effect!n");
}
int main()
{
char a[f()];
printf("%zdn", 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");
^

因此,gccseems将其内置版本的printf视为常量表达式。gcc在此处记录内置,但不记录这种情况,其中非constexpr函数的内置可以被视为常量表达式。

如果确实如此:

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

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

更新

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

#include <cmath>
#include <cstdio>
constexpr double f()
{
return std::pow( 2.0, 2.0 ) ;
}
int main()
{
constexpr double x = f() ;
printf( "%fn", x ) ;
}

编译和构建时没有警告或错误(实时查看),但添加-fno-builtin会生成错误(实时查看)。注意:为什么数学函数在 C++11 中不是 constexpr:

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

是的,gcc正在考虑将一些内置函数视为constexpr,即使标准没有明确标记它们。我们可以在gcc错误报告 [C++0x] 中找到专门与cmath中的数学函数相关的讨论 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 关于函数(和内置),由于上述问题,重新打开上述错误报告:

我相信我们应该根据实际解决方案重新打开该错误 LWG 2013(禁止添加 constexpr)。

FE 不应将内置函数视为严格一致性的 constexpr 模式。

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

GCC 不认为f()是一个常量表达式。查看您链接的第一个示例程序的诊断程序:

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

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

如果更改程序以强制f()为常量表达式:

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

GCC 确实报告了一个错误:

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