12

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

这个问题为什么我可以在 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 函数的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 ) ;
}

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

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

2 回答 2

6

是的,即使标准没有明确地将它们标记为 constexpr,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 revisited: constexpr on functions (and builtins) 其中由于上述问题而讨论了重新打开上述错误报告:

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

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

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

于 2015-01-12T16:51:17.693 回答
4

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

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 确实报告了一个错误

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