C ++ 20:非类型模板参数中的非捕获lambda

时间:2020-01-16 00:33:07

标签: c++ templates lambda c++20

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

3 个答案:

答案 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)

模板参数必须是constexpr变量。

有一个有关lambda的相关建议N4487

我不知道它是否进入了C ++ 20。