【问题标题】:Parameter pack expansion with lambda in C++20在 C++20 中使用 lambda 进行参数包扩展
【发布时间】:2021-06-11 22:49:10
【问题描述】:

案例 1

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

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

Clang 和 MSVC 接受此代码,但 GCC rejects 带有:

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

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

案例 2

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

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

在这种情况下,Clang 和 MSVC 仍然接受此代码,并且 GCC rejects 它带有相同的错误消息。这只是同一个错误吗?

案例 3

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

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

这次三个编译器all reject出现同样的错误信息:

expansion pattern '<lambda>' contains no parameter packs

与案例 1 相比有什么不同吗?或者这是一个常见的错误?


更新:

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

【问题讨论】:

  • 这三个都应该被接受;如果他们不这样做,我会认为这是标准中的错误。它们是每个表达式,这些表达式(在某种递归级别)包含一个参数包,并且这些包一旦扩展就有意义。但是,指定参数包如何扩展是一件很痛苦的事情,而且我相信标准中存在一些错误,导致它们无法正常工作。 (当然,f(...) 调用可能是 UB(或者可能不是,我必须看看空 lambda 和 ... 是如何交互的),但这并不重要)
  • @Yakk-AdamNevraumont - 你确定(这是正确的)关于第三种情况吗?我的意思是:[]&lt;Args&gt;(){} 是什么?不应该是[]&lt;typename Args&gt;(){},在那种情况下,定义一个新的typename模板参数,隐藏函数Args列表并且无法解包?
  • @max66 非类型模板参数
  • @Yakk-AdamNevraumont - 呃...你是对的。
  • 这个有趣问题的状态更新:1) 在 g++ 11.1 中已修复。 2) 在 g++ 11.1 中未修复,因此与 1 中的错误不同。3) 在 MSVC 19.29 中修复。

标签: c++ lambda language-lawyer variadic-templates c++20


【解决方案1】:

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

如果 lambda-expression 具有任何通用参数类型占位符 (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 表达式确定。编译器绝对可以使用类似于以下的代码来满足每种情况:

案例一

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。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-10
    • 2017-09-09
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 2021-06-16
    相关资源
    最近更新 更多