`constexpr` 函数可以在 C++ 中前向声明吗?

Can `constexpr` function be forward declared in C++?

我可以在给出定义之前在 C++ 中声明一个 constexpr 函数吗?

考虑一个例子:

constexpr int foo(int);
constexpr int bar() { return foo(42); }
constexpr int foo(int) { return 1; }

static_assert(bar() == 1);

其实所有编译器都支持,demo:https://gcc.godbolt.org/z/o4PThejso

但是如果在模板中转换函数 foo:

constexpr int foo(auto);
constexpr int bar() { return foo(42); }
constexpr int foo(auto) { return 1; }

static_assert(bar() == 1);

然后Clang拒绝接受,说https://gcc.godbolt.org/z/EG7cG9KTM:

<source>:5:15: error: static_assert expression is not an integral constant expression
static_assert(bar() == 1);
              ^~~~~~~~~~
<source>:2:30: note: undefined function 'foo<int>' cannot be used in a constant expression
constexpr int bar() { return foo(42); }
                             ^
<source>:5:15: note: in call to 'bar()'
static_assert(bar() == 1);

它仍然是有效的 C++ 代码还是 Clang 错误?

这是核心问题2166

Section: 7.7 [expr.const] Status: drafting Submitter: Howard Hinnant Date: 2015-08-05

According to 7.7 [expr.const] bullet 2.3, an expression is a constant expression unless (among other reasons) it would evaluate

an invocation of an undefined constexpr function or an undefined constexpr constructor;

这没有解决 constexpr 所在点的问题 必须定义函数。意图,为了让 相互递归的 constexpr 函数,是函数必须是 在最终导致的最外层评估之前定义 调用,但这没有明确说明。

换句话说,对于这种情况,标准是不明确的,所以每个编译器都可以自由地接受或拒绝它认为合适的代码。

对于 Clang,auto 参数是通过将函数转换为模板来实现的。这使得编译器更难维护 constexpr 上下文。也不是说不能实现(毕竟GCC没问题),只是2166解决之前不是bug

同时,您可以通过使 bar() 也成为模板或从 foo() 中删除 auto 来编译代码。

这是CWG2166:

2166. Unclear meaning of “undefined constexpr function”

According to 7.7 [expr.const] bullet 2.3, an expression is a constant expression unless (among other reasons) it would evaluate

  • an invocation of an undefined constexpr function or an undefined constexpr constructor;

This does not address the question of the point at which a constexpr function must be defined. The intent, in order to allow mutually-recursive constexpr functions, was that the function must be defined prior to the outermost evaluation that eventually results in the invocation, but this is not clearly stated.