[英]C++20: Non-capturing lambda in non-type template parameter
Does C++20 allow a non-capturing lambda decayed to a function pointer to be passed directly as a non-type template parameter? C++20 是否允许衰减为函数指针的非捕获 lambda 直接作为非类型模板参数传递? If so, what is the correct syntax?
如果是这样,正确的语法是什么?
I have tried the following code in various versions of clang and gcc using -std=c++2a
.我已经使用
-std=c++2a
在各种版本的 clang 和 gcc 中尝试了以下代码。
#include <iostream>
template<auto f>
struct S {
static void invoke(int x) { f(x); }
};
using X = S<+[](int x) -> void { std::cout << x << " hello\n"; }>;
int main()
{
X::invoke(42);
}
gcc compiles the code without complaint and the code runs as expected. gcc 毫无怨言地编译代码,代码按预期运行。
clang fails compilation with the following error: clang 编译失败并出现以下错误:
error: a lambda expression cannot appear in this context
using X = S<+[](int x) -> void { std::cout << x << " hello\n"; }>;
^
Here is the full code (online versions):这是完整的代码(在线版本):
Clang 10.0.0 HEAD: https://wandbox.org/permlink/n5eKQ4kQqSpDpr4k Clang 10.0.0 头: https : //wandbox.org/permlink/n5eKQ4kQqSpDpr4k
Gcc 10.0.0 HEAD 20200113: https://wandbox.org/permlink/vJ44sdMtwCKAFU64 Gcc 10.0.0 HEAD 20200113: https ://wandbox.org/permlink/vJ44sdMtwCKAFU64
Does C++20 allow a non-capturing lambda decayed to a function pointer to be passed directly as a non-type template parameter?
C++20 是否允许衰减为函数指针的非捕获 lambda 直接作为非类型模板参数传递?
Yes.是的。
Indeed, you can go one step further - you don't even need to convert the lambda to a function pointer.事实上,您可以更进一步——您甚至不需要将 lambda 转换为函数指针。 You can just provide the lambda.
您可以只提供 lambda。 This is valid C++20:
这是有效的 C++20:
using Y = S<[](int x) -> void { std::cout << x << " hello\n"; }>;
The rule we have in C++20 is that lambdas are now allowed in unevaluated contexts ( P0315 ).我们在 C++20 中的规则是现在允许在未评估的上下文中使用 lambdas ( P0315 )。 Among many other wording changes there, this paper struck the rule that prevented lambdas from being used in template arguments ( C++17's [expr.prim.lambda]/2 ):
在那里的许多其他措辞更改中,本文打破了阻止在模板参数中使用 lambda 的规则( C++17 的 [expr.prim.lambda]/2 ):
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 表达式不应出现在未计算的操作数、模板参数、别名声明、typedef 声明中,或者出现在函数体和默认参数之外的函数或函数模板的声明中。
That clause does not exist anymore in C++20.该子句在 C++20 中不再存在。
Removing this restriction allows the lambda to be used as a template argument, and the conversion from captureless lambda to function pointer was already constexpr in C++17.取消这个限制允许将 lambda 用作模板参数,并且从无捕获 lambda 到函数指针的转换在 C++17 中已经是 constexpr。 clang simply does not implement this feature yet (
using T = decltype([]{});
compiles on gcc, not yet on clang). clang 还没有实现这个特性(
using T = decltype([]{});
在 gcc 上编译,还没有在 clang 上编译)。 I wouldn't call this a clang bug yet, it's just a clang not-yet-implemented-feature (lambdas in unevaluated contexts is not yet listed as implemented in the cppreference compiler support page ).我还不会称其为 clang 错误,它只是一个尚未实现的 clang 功能(未评估上下文中的 lambdas 尚未在cppreference 编译器支持页面中列出为已实现)。
C++20 non-type template parameters ( P1907 ) allows even dropping the +
because captureless lambdas count as structural types ( [temp.param]/7 ) by way of simply not having any data members at all. C++20 非类型模板参数 ( P1907 ) 甚至允许删除
+
因为无捕获的 lambda 算作结构类型( [temp.param]/7 ),因为根本没有任何数据成员。
If the rules on this haven't changed since C++17 then using a lambda as a template parameter is not allowed for the same reason that using a string literal is not allowed.如果这方面的规则自 C++17 以来没有改变,则不允许使用 lambda 作为模板参数,原因与不允许使用字符串文字相同。 Every lambda has a different type and every string literal refers to a different object.
每个 lambda 都有不同的类型,每个字符串字面量都指向不同的对象。 What changed in C++17 is that closure objects are now
constexpr
. C++17 中的变化是闭包对象现在是
constexpr
。 To use a string literal or a lambda as a template parameter, the object must have external linkage.要使用字符串文字或 lambda 作为模板参数,对象必须具有外部链接。 So this is allowed in C++17.
所以这在 C++17 中是允许的。
template <auto>
struct S {};
constexpr const char string[] = "String literal";
constexpr auto lambda = []{};
S<string> a;
S<+lambda> b;
The closure object itself cannot be used as a template parameter (so you can't do S<lambda>
) but this might have changed in C++20 with three-way comparisons.闭包对象本身不能用作模板参数(因此您不能执行
S<lambda>
),但这可能在 C++20 中通过三向比较发生了变化。 The reason the objects must have external linkage is because it kind of breaks templates.对象必须具有外部链接的原因是因为它破坏了模板。
S<+[]{}>
and S<+[]{}>
would be considered different types even though they look the same (similarly with S<"">
). S<+[]{}>
和S<+[]{}>
将被视为不同的类型,即使它们看起来相同(与S<"">
类似)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.