考虑以下代码:
#include <stdio.h>
constexpr int f()
{
return printf("a side effect!\n");
}
int main()
{
char a[f()];
printf("%zd\n", sizeof a);
}
我本以为编译器会抱怨
printf
中对 f
的调用,因为 f
应该是 constexpr
,但 printf
不是。为什么程序编译并打印15?
该程序是格式错误,根据C++11标准草案部分
7.1.5
constexpr说明符段落5不需要诊断,其中说:对于 constexpr 函数,如果不存在这样的函数参数值 函数调用替换会产生一个常量 表达式(5.19),程序格式错误;无需诊断。
并提供以下示例:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
和第
5.19
第2段说:条件表达式是核心常量表达式,除非它 涉及以下内容之一作为潜在评估的子表达式 [...]
并包括:
— 对 constexpr 构造函数之外的函数的调用 文字类或 constexpr 函数 [注意:重载解析 (13.3)照常应用——尾注];
在这种情况下我们可能更喜欢诊断,这可能只是一个疏忽,我有一个类似情况的错误报告,其中
gcc
不会产生错误,但我们可能希望它:编译器是否允许有余地它认为常量表达式中的未定义行为是什么?。 更新
使用
-fno-builtin
标志将导致
gcc
生成以下错误: error: call to non-constexpr function 'int printf(const char*, ...)'
return printf("a side effect!\n");
^
所以
gcc
确实考虑了这个格式错误,只是在使用内置版本的
printf
时忽略了它。
尽管使用
-pedantic
的方式有些不一致,但会产生以下警告:warning: ISO C++ forbids variable length array 'a' [-Wvla]
char a[f()];
^
请注意,使用
f()
来初始化 constexpr 变量:constexpr int x = f() ;
确实产生错误:
error: 'printf(((const char*)"a side effect!\012"))' is not a constant expression
。该程序自 C++23 起合法。
C++23 允许您将(几乎)任何函数标记为
constexpr
,即使不可能在编译时求值。在您的情况下,该函数将在运行时运行。仅当您尝试“强制”它在编译时运行时,才会出现错误,例如将结果分配给 constexpr
变量。