C++ gcc是否认为非常量表达式函数的内置函数是常量表达式

C++ gcc是否认为非常量表达式函数的内置函数是常量表达式,c++,gcc,c++11,language-lawyer,constant-expression,C++,Gcc,C++11,Language Lawyer,Constant Expression,请参阅更新以获得问题的更好示例。原始代码有一系列问题,这些问题使情况变得混乱: 这个问题给出了以下代码 #include <stdio.h> constexpr int f() { return printf("a side effect!\n"); } int main() { char a[f()]; printf("%zd\n", sizeof a); } 因此似乎认为gcc的内置版本printf是一个常量表达式gcc,但没有说明非constexp

请参阅更新以获得问题的更好示例。原始代码有一系列问题,这些问题使情况变得混乱:

这个问题给出了以下代码

#include <stdio.h>

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

int main()
{
    char a[f()];
    printf("%zd\n", sizeof a);
}
因此
似乎
认为
gcc
的内置版本
printf
是一个常量表达式
gcc
,但没有说明非constexpr函数的内置函数可以被视为常量表达式的情况

如果情况确实如此:

  • 是否允许编译器执行此操作
  • 如果他们被允许,他们不需要记录下来以符合要求吗
  • 这是否可以被视为一种扩展,如果是这样的话,这似乎需要一个警告,正如
    1.4节
    实施合规性第8段所述(重点):
一致性实现可能具有扩展(包括附加的库函数),前提是它们不会改变任何格式良好的程序的行为根据本国际标准,需要实施来诊断使用此类扩展的程序,这些扩展的格式不正确。然而,这样做之后,他们就可以编译和执行这样的程序了

更新

正如Casey指出的,原始问题中存在一些问题,这使得它成为一个糟糕的例子。一个简单的例子是使用非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 ) ;
}

GCC没有考虑<代码> f-()/代码>是一个常量表达式。查看以下诊断:

:

main.cpp:在函数“int main()”中: main.cpp:10:32:in-constexpr扩展'f()' main.cpp:5:41:错误:“printf(((const char*)“副作用!\012”)”不是常量表达式 返回printf(“副作用!\n”);
^ 是的,
gcc
正在考虑将一些作为constexpr,即使标准没有明确地将它们标记为constexpr。我们可以在
gcc
bug报告中找到专门与
cmath
中的数学函数相关的讨论,其中指出:

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

指其最初提议的决议是将以下内容添加到第17.6.5.6节[施工图功能](重点放在后面):

[…]此外,一个实现可以声明任何要执行的函数 constexpr,如果该函数的定义满足 限制[……]

但在C++11之后,分辨率被颠倒,最终分辨率为:

[…]实现不应声明任何标准库函数 作为constexpr的签名,除非明确 必需。[..]

这是目前(在C++14中)的情况,据我所知,这在C++11中是不一致的,因为它改变了可观察的行为,因此通过“仿佛”规则是不允许的

Jonathan Wakely指出了一个
libstdc++
邮件列表讨论:由于上述问题,讨论了重新打开上述bug报告的问题:

我认为我们应该根据实际解决方案重新打开bug LWG 2013年版(禁止添加constexpr)

FE不应严格按照constexpr处理内置项 模式

我们应该将_GLIBCXX_CONSTEXPR完全从 以严格的ANSI为条件


你认为gcc到底违反了什么?是不是[dcl.constexpr]/5“对于一个constexpr函数,如果没有函数参数值存在,函数调用替换将产生一个常量表达式(5.19),那么程序是错误的;不需要诊断。”?注意:我不太明白为什么在gcc拒绝它时,你说“gcc 4.8.2允许它”,调用
f()
不是一个常量表达式。但是,它没有拒绝的是函数
f
本身的定义,对于它来说,不需要诊断。我不相信它将内置函数视为常量表达式。您没有提供一个例子来证明这一点(调用
f()
,导致调用
printf(“副作用!\n”)
并不是一个常量表达式)。[续..]当切换到
-fno-builtin
时,它还诊断
f
本身的病态。现在的问题是:“gcc用不同的开关显示不同的行为是一致的吗?”还是“gcc接受非常量表达式作为数组绑定是一致的?”还是“gcc在不拒绝
f
时是一致的?”嗯,这个例子在我看来更好。
f
格式仍然不正确,不需要诊断,但
x=f()
格式不正确,我认为需要诊断。接受该程序是一个扩展,不产生任何关于它的消息是不合格的。关于strlen的例子,我必须再看一次另一个例子。@ShafikYaghmour
strlen
没有副作用。很可能是
constexpr
,我要说的是,对于gcc+args的特殊风格,它是
constexpr
。有趣的是,C++1y(无论如何,N3936)17.6.5.6[constexpr.functions]/1可能禁止这种行为:“本标准明确要求某些标准库函数为constexpr(7.1.5)。实现不应将任何标准库函数签名声明为constexpr,除非明确要求。”如果它生成错误:
error:call to non-constexpr function'size_t strlen(const char*)”
@ShafikYaghmour是的,我知道-因此我的声明是“针对特定风格的gcc+args”。+1最初的示例发生了太多事情,所以我更新了我的问题,并添加了一个更简单的示例,使用
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 ) ;
}
error: call to non-constexpr function 'double pow(double, double)'
     return std::pow( 2.0, 2.0 ) ;
                               ^
main.cpp: In function 'int main()': main.cpp:10:19: warning: ISO C++ forbids variable length array 'a' [-Wvla] char a[f()]; ^
int main() {
    constexpr int size = f();
    char a[size];
    printf("%zd\n", sizeof a);
}
main.cpp: In function 'int main()': main.cpp:10:32: in constexpr expansion of 'f()' main.cpp:5:41: error: 'printf(((const char*)"a side effect!\012"))' is not a constant expression return printf("a side effect!\n"); ^