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

2021-12-13 00:00:00 language-lawyer templates c++ c++17

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

I know it's like opening the Pandora box but it doesn't stop bothering me. Consider a simple example:

#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);
}

我知道 lambdas 不能在未评估的上下文中声明,但显然这里不是这种情况.还有什么更奇怪的 clang 5.0.0(我猜它首先部分支持 constexpr lambda)编译它.

I know lambdas cannot be declared inside unevaluated context, but obviously this is not the case here. What is even more weird clang 5.0.0 (which, I guess, first partially supports constexpr lambda) does compile it.

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

Is it a compiler bug or will C++17 allow this?

推荐答案

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

No, that is a compiler bug. gcc 7.1 correctly rejects the code.

[expr.prim.lambda]/2:

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

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 表达式不能出现在模板参数列表中.

As you can see from the part that I marked as bold, a lambda expression cannot appear in a template argument list.

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

This is also made clear in a subsequent note:

[ 注意:目的是防止 lambda 出现在签名中.― 尾注 ]

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

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

If I were to guess, I would say that the bug comes about because starting with C++17, lambdas are implicitly constexpr, which makes them valid to be called in compile time expressions, like template arguments. But actually defining a lambda in a template argument is still illegal.

请注意,此限制已在 C++20 中取消.:)

Note that this restriction has been lifted in C++20. :)

相关文章