什么决定了一个 constexpr 函数是否是常量表达式?

What determines whether a constexpr function is a constant expression?

(据我所知,使用的编译器是带有 c++17 的 gcc(在 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' cannot result in a constant expression.

据我了解,这是因为函数 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';
}

此代码在功能上是相同的,并且可以编译,即使 increment 仍然不是 constexpr。我不明白 [0, 1) 范围内的 for 循环怎么可能导致编译器意识到函数 f 实际上是一个 constexpr.

如果有人能就 c++ 中的 constexpr 和这种明显的不一致提供一些见解,我将不胜感激。

标准要求 constexpr 函数实际上可以在编译时对 一些 参数集而不是全部参数进行评估。它不需要编译器来诊断 constexpr 函数执行某些在某些情况下可能是非编译时的事情,甚至不需要诊断这样的函数是否具有这样的一组参数。这避免了他们不得不解决停机问题。

你实际上并不是在编译时“调用”f

如果您的主要功能包括: static_assert(f() == 1, "f() returned 1"); 我怀疑您会收到“f() 不是常量表达式”错误。

这是一个related question

根据 [dcl.constexpr]/6:

,这两个程序都“格式错误,无需诊断”

For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, an evaluated subexpression of the initialization full-expression of some constant-initialized object ([basic.start.static]), the program is ill-formed, no diagnostic required.

有点奇怪 gcc 只是没有注意到第二个程序的问题,但它仍然符合要求。

请注意,如果在实际需要常量表达式的上下文中使用 f,则需要进行诊断,例如 constexpr int n = f();

某些事情在 constexpr 函数中是绝对不允许的。这些确实需要诊断(通常是错误消息),即使该函数从未在常量表达式中使用 - 参见 。但是问题中的程序没有违反任何这些更严格的规则。

由于您没有在常量表达式中调用 f,您的问题是询问编译器是否 需要 来诊断 f 不能'仅根据其 定义.

在常量表达式中调用 t

枚举constexpr函数定义的要求here:

The definition of a constexpr function shall satisfy the following requirements:

(3.1) its return type (if any) shall be a literal type;

(3.2) each of its parameter types shall be a literal type;

(3.3) it shall not be a coroutine;

(3.4) if the function is a constructor or destructor, its class shall not have any virtual base classes;

(3.5) its function-body shall not enclose

(3.5.1) a goto statement,

(3.5.2) an identifier label,

(3.5.3) a definition of a variable of non-literal type or of static or thread storage duration.

可以看出,f的定义并没有违反列表中的任何要求。因此,如果编译器选择不对此进行诊断,那么它就是符合标准的。

正如 中指出的那样,constexpr 函数如 f 不能在常量表达式中调用,但不能被诊断为格式错误-无需诊断。