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

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

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

#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)does compile it.

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

不,那是一个编译器错误。 gcc 7.1 正确地拒绝了代码。

[expr.prim.lambda]/2:

A lambda-expression is a prvalue whose result object is called the closure object. A lambda-expression shall not appear in an unevaluated operand, in a template-argument, in an alias-declaration, in a typedef declaration, or in the declaration of a function or function template outside its function body and default arguments.

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

这一点在随后的注释中也有明确说明:

[ Note: The intention is to prevent lambdas from appearing in a signature. — end note ]

如果我猜的话,我会说这个错误是因为从 C++17 开始,lambdas 是隐式的 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);
}