[英]What determines whether a constexpr function is a constant expression?
(据我所知,使用的编译器是 gcc 和 c++17 (在 Visual Studio 中很难找到这个))
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
increment( v );
return v;
}
int main( )
{
cout << f( ) << '\n';
}
上面的代码给出了编译错误:
constexpr function 'f' 不能产生常量表达式。
据我了解,这是因为 function increment
不是 constexpr。 让我感到困惑的是,以下代码编译得很好:
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
for( int i = 0; i < 1; ++i )
{
increment( v );
}
return v;
}
int main( )
{
cout << f( ) << '\n';
}
这段代码在功能上是相同的,并且可以编译,即使增量仍然不是 constexpr。 我不明白通过范围 [0, 1) 的 for 循环如何导致编译器意识到 function f
实际上是 constexpr。
如果有人可以对 c++ 中的 constexpr 和这种明显的不一致提供一些见解,我将不胜感激。
根据[dcl.constexpr]/6 ,这两个程序都是“格式错误,不需要诊断”:
对于既不是默认值也不是模板的 constexpr function 或 constexpr 构造函数,如果不存在参数值,则 function 或构造函数的调用可以是核心常量表达式的求值子表达式,或者,对于构造函数,求值子表达式一些常量初始化的 object ( [basic.start.static] ) 的初始化完整表达式,程序格式错误,不需要诊断。
有点奇怪,gcc 只是没有注意到第二个程序的问题,但它仍然符合要求。
请注意,如果在实际需要常量表达式的上下文中使用f
,则需要进行诊断,例如constexpr int n = f();
.
有些事情在 constexpr function 中是不允许的。 这些确实需要诊断(通常是错误消息),即使 function 从未在常量表达式中使用 - 请参阅cigien 的答案。 但是问题中的程序不违反任何这些更严格的规则。
由于您没有在常量表达式中调用f
,因此您的问题是询问编译器是否需要仅根据其定义来诊断无法在常量表达式中调用f
。
这里列举了对constexpr
function定义的要求:
constexpr function 的定义应满足以下要求:
(3.1) 其返回类型(如果有)应为文字类型;
(3.2) 其每个参数类型应为字面量类型;
(3.3) 不能是协程;
(3.4) 如果 function 是构造函数或析构函数,其 class 不得有任何虚拟基类;
(3.5) 其函数体不得包含
(3.5.1) goto 语句,
(3.5.2) 标识符 label,
(3.5.3) 非文字类型或 static 或线程存储持续时间的变量的定义。
可以看出, f
的定义没有违反列表中的任何要求。 因此,如果编译器选择不诊断这一点,它就是符合标准的。
正如aschepler 的回答中指出的那样,像f
这样的constexpr
函数不能在常量表达式中调用,但不能被诊断,被认为是格式错误的,不需要诊断。
您实际上并没有在编译时“调用” f
。
如果您的主要 function 包括: static_assert(f() == 1, "f() returned 1");
我怀疑您会收到“f() 不是常量表达式”错误。
这是一个相关的问题
该标准要求constexpr
function 实际上可以在编译时对某些参数集进行评估,但不是全部。 它不需要编译器来诊断constexpr
function 在某些情况下执行某些可能是非编译时的事情,甚至不需要编译器来诊断这样的 function 是否具有这样的一组参数。 这避免了他们不得不解决停机问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.