使用引用参数嵌套调用 consteval 函数

2023-11-24

下面的程序

template<class T>
consteval auto foo(const T&) {
   return 0;
}

template<class T>
consteval auto bar(const T& t) {
   auto n = foo(t);
   return n;
}

int main() {
   static_assert(foo("abc") == 0);
   static_assert(bar("abc") == 0);
}

在 GCC 中构建得很好,但 Clang 拒绝了它,并显示以下消息:

error: call to consteval function 'foo<char[4]>' is not a constant expression
note: in instantiation of function template specialization 'bar<char[4]>' requested here
   static_assert(bar("abc") == 0);
note: function parameter 't' with unknown value cannot be used in a constant expression
   auto n = foo(t);

Demo: https://gcc.godbolt.org/z/M6GPnYdqb

这是 Clang 中的一些错误吗?


这是一个 clang bug。 gcc 和 msvc 接受它是正确的。

有两个相关规则有问题:

All 立即调用必须是常量表达式。这来自[表达式常量]/13:

表达式或转换位于立即函数上下文如果可能进行评估并且:

  • 其最内层封闭的非块作用域是立即函数的函数参数作用域,或者
  • 它的封闭语句被 consteval if 语句 ([stmt.if]) 的复合语句括起来 ([stmt.pre])。

表达式或转换是立即调用如果它是立即函数的潜在评估显式或隐式调用并且不在立即函数上下文中。立即调用应是常量表达式。

并且在常量表达式中不允许接触未知引用(这是[表达式常量]/5.13):

表达式 E 是核心常量表达式,除非对 E 的求值遵循抽象机 ([intro.execution]) 的规则,将求值以下其中一项:[...]

  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
    • 它可用于常量表达式或
    • 它的生命周期从E的评估开始;

有关后一条规则的更多信息,请参阅我在constexpr 数组大小问题 and 我解决这个问题的建议(希望适用于 C++23)。


好吧,回到问题上来。foo显然很好,它没有做任何事情。

In bar, 我们称之为foo(t)。这不是一个常量表达式(因为t是一个未知的引用),但我们处于直接函数上下文中(因为bar is consteval),所以没关系foo(t)不是常量表达式。重要的是bar("abc")是一个常量表达式(因为is立即调用),并且我们没有违反任何规则。虽然很微妙,但还是有参考价值t here does其生命周期开始于评估E- 自从E这是电话bar("abc"), not电话foo(t).

如果您标记bar constexpr代替consteval,那么foo(t)它内部的调用变成了立即调用,现在它不是常量表达式这一事实是相关的。在这种情况下,所有三个编译器都正确拒绝。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用引用参数嵌套调用 consteval 函数 的相关文章

随机推荐