C ++ 20是否允许将衰减到函数指针的非捕获lambda作为非类型模板参数直接传递?如果是这样,正确的语法是什么?
我已经使用-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会毫无保留地编译代码,并且代码会按预期运行。
clang编译失败,出现以下错误:
error: a lambda expression cannot appear in this context
using X = S<+[](int x) -> void { std::cout << x << " hello\n"; }>;
^
以下是完整代码(在线版本):
Clang 10.0.0 HEAD:https://wandbox.org/permlink/n5eKQ4kQqSpDpr4k
Gcc 10.0.0 HEAD 20200113:https://wandbox.org/permlink/vJ44sdMtwCKAFU64
答案 0 :(得分:9)
C ++ 20是否允许将衰减到函数指针的非捕获lambda作为非类型模板参数直接传递?
是的
实际上,您可以更进一步-甚至不需要将lambda转换为函数指针。您可以只提供lambda。这是有效的C ++ 20:
using Y = S<[](int x) -> void { std::cout << x << " hello\n"; }>;
C ++ 20中的规则是,现在可以在未评估的上下文(P0315)中使用lambda。除了那里的许多其他措辞更改之外,本文还打破了禁止在模板参数(C++17's [expr.prim.lambda]/2)中使用lambda的规则:
lambda表达式不应出现在未评估的操作数中,在 template-argument 中的别名声明中,在typedef声明中或在函数主体和默认参数之外的函数或函数模板的声明中。
该子句在C ++ 20中不再存在。
取消此限制,就可以将lambda用作模板参数,并且从无捕获lambda到函数指针的转换已在C ++ 17中进行了constexpr。 clang根本不实现此功能(using T = decltype([]{});
在gcc上编译,而不在clang上编译)。我还不会将其称为clang错误,它只是尚未实现的clang特征(未评估上下文中的lambda尚未在cppreference compiler support page中列出为已实现)。
C ++ 20非类型模板参数(P1907)甚至可以丢弃+
,因为无捕获的lambda算作结构类型([temp.param]/7)根本没有任何数据成员的方式。
答案 1 :(得分:0)
如果自C ++ 17以来此规则没有改变,则不允许使用lambda作为模板参数,其原因与不允许使用字符串文字相同。每个lambda都有不同的类型,每个字符串文字都指向不同的对象。 C ++ 17中的变化是闭包对象现在为constexpr
。要将字符串文字或lambda用作模板参数,该对象必须具有外部链接。因此,在C ++ 17中是允许的。
template <auto>
struct S {};
constexpr const char string[] = "String literal";
constexpr auto lambda = []{};
S<string> a;
S<+lambda> b;
闭包对象本身不能用作模板参数(因此您无法执行S<lambda>
),但是在C ++ 20中,通过三向比较,这可能已更改。对象必须具有外部链接的原因是因为它破坏了模板。即使S<+[]{}>
和S<+[]{}>
看起来一样(与S<"">
类似),也会被认为是不同的类型。
答案 2 :(得分:-1)