我可以在模板参数中声明一个 constexpr lambda 吗?

Will I be able to declare a constexpr lambda inside a template parameter?

本文关键字:一个 constexpr lambda 参数 声明 我可以      更新时间:2023-10-16

我知道这就像打开潘多拉盒子一样,但它并没有停止困扰我。考虑一个简单的例子:

#include <type_traits>
template <auto>
struct Foo: std::false_type { };
template <>
struct Foo<[](){return 1;}()>:std::true_type { };
int main() {
static_assert(Foo<1>::value);
}

我知道 lambda 不能在未评估的上下文中声明,但显然这里的情况并非如此。更奇怪的是 clang 5.0.0(我猜,它首先部分支持 constexpr lambda)确实编译了它。

这是一个编译器错误还是 C++17 允许这样做?

不,这是一个编译器错误。 GCC 7.1 正确拒绝代码。

[expr.prim.lambda]/2:

lambda 表达式是一个 prvalue,其结果对象称为闭包对象。lambda 表达式不得出现在未求值的操作数、模板参数、别名声明、typedef 声明中,或者出现在函数体和默认参数之外的函数或函数模板的声明中。

从我标记为粗体的部分可以看出,lambda 表达式不能出现在模板参数列表中。

这在随后的说明中也明确指出:

[ 注意:目的是防止 lambda 出现在签名中。

如果我猜,我会说这个错误的出现是因为从 C++17 开始,lambda 是隐式constexpr的,这使得它们在编译时表达式(如模板参数)中调用是有效的。但实际上在模板参数中定义 lambda 仍然是非法的。


请注意,此限制已在 C++20 中解除。 :)

在 C++17 中,您可以将指向 lambda 函数的指针作为具有函数指针类型的模板参数传递:

# include <cassert>
template<int(*fn)()>
int get_twice()
{
return fn() * 2;
}
int main()
{
int result = get_twice <+[]() { return 42; }> ();
assert(result == 84);
}