首页 > 解决方案 > 在 C++20 中使用 lambda 进行参数包扩展

问题描述

案例一

考虑 lambda 说明符中的以下包扩展noexcept

template <bool... B> 
auto g() {  
  ([]() noexcept(B) {}, ...);  
}

Clang 和 MSVC 接受此代码,但 GCC拒绝

error: expansion pattern '<lambda>' contains no parameter packs

这是一个有效的代码吗?我应该信任哪个编译器?

案例二

考虑 lambda 中的以下包扩展requires-clause

template <bool... B> 
auto g() {  
  ([](auto) requires(B) {}, ...);  
}

在这种情况下,Clang 和 MSVC 仍然接受此代码,而 GCC以相同的错误消息拒绝它。这只是同一个错误吗?

案例3

考虑 lambda 模板列表中的以下包扩展:

template <typename... Args> 
void g(Args...) {
  ([]<Args>(){}, ...);  
}

这次三个编译器都拒绝了相同的错误消息:

expansion pattern '<lambda>' contains no parameter packs

与案例1相比有区别吗?或者这是一个常见的错误?


更新:

GCC 修复了99584中的 case 1,MSVC 修复了this中的 case 3 。

标签: c++lambdalanguage-lawyervariadic-templatesc++20

解决方案


有趣的练习!查看 C++20 草案,标准首先区分了泛型 lambdas (7.5.5.5):

如果lambda 表达式具有任何通用参数类型占位符 (9.2.8.5),或者 lambda 具有模板参数列表,则lambda 是通用 lambda

int i = [](int i, auto a) { return i; }(3, 4); // OK: a generic lambda
int j = []<class T>(T t, int i) { return i; }(3, 4); // OK: a generic lambda

因此,案例 1 具有非泛型 lambda,而案例 2 和 3 具有泛型 lambda。它不区分类型和非类型模板参数,所以我认为我同意 Yakk - Adam Nevraumont 的观点,这三个都应该是可以接受的。

Lambda 由一个未命名的唯一闭包类型表示,该闭包类型具有函数调用运算符 ( operator())(在泛型 lambda 的情况下为模板),其签名和约束由 lambda 表达式确定。编译器绝对可以使用类似于以下的代码来满足每种情况:

情况1

template<bool B>
struct closure {
    auto operator()() const noexcept(B) {}
};

template <bool... B> 
auto g() {  
  (closure<B>{}, ...);  
}

案例2

template<bool B>
struct closure {
    auto operator()(auto) const requires(B) {}
};

template <bool... B> 
auto g() {  
  (closure<B>{}, ...);  
}

案例3

template<typename T>
struct closure {
    template<T>
    auto operator()() const {}
};

template <typename... Args> 
void g(Args...) {
  (closure<Args>{}, ...);  
}

这些都在我的机器上编译,目前使用 gcc 11.2.1。


推荐阅读