在基于范围的循环中使用 lambda 的初始值设定项列表

Using an initializer list of lambdas in a range-based loop

本文关键字:lambda 列表 于范围 范围 循环      更新时间:2023-10-16

使用 gcc 4.9 -std=c++14,我尝试制作一个 lambda 向量:

vector<function<void ()>> v = {[]{cout << "foo";}, []{cout << "bar";}};
for (auto&& a: v) a();

而且效果很好。然后我尝试将 lambda 的初始值设定项列表直接传递给基于范围的 for:

for (auto&& a: {[]{cout << "foo";}, []{cout << "bar";}}) a();

我得到了:

error: unable to deduce 'std::initializer_list<auto>&&' from '{<lambda closure object>main()::<lambda()>{}, <lambda closure object>main()::<lambda()>{}}'

从错误消息的外观来看,我做了一个疯狂的猜测,这可能是因为"lambda 闭包对象"是内置的语言术语,而不是 std::function 的直接等价物(所以没有真正的类型)。

造成这种情况的更深层次原因是什么?此外,这是否与实现相关,或者这种行为是由规范决定的?

每个 lambda 都有自己独特的类型。因此,您可能不会从不同类型的 lambda 构建 std::initializer_list。

根据C++标准(5.1.2 Lambda 表达式)

3 lambda 表达式的类型(这也是 闭包对象)是一个唯一的、未命名的非联合类类型,称为 闭包类型 — 其属性如下所述。

6 非泛型 lambda 表达式的闭包类型,没有 lambda-capture 具有公共非虚拟非显式常量转换 函数到指针到具有C++语言链接的函数 (7.5) 具有 与闭包类型的函数相同的参数和返回类型 呼叫接线员。

每个 lamdba 都有自己的类型,因此编译器无法推断出initializer_list的类型。

你必须告诉你想要哪种类型:

  • 对于每个 lambda:

    • 由于您的 lambda 不捕获变量,因此您可以将变量衰减为指针以使用+,如下所示:

      for (auto&& a: {+[]{std::cout << "foo";}, +[]{std::cout << "bar";}}) a();
      
    • 使用function<void()>

      for (auto&& a: {std::function<void()>([]{std::cout << "foo";}),
                      std::function<void()>([]{std::cout << "bar";})}) a();
      
  • 对于initializer_list:

    for (auto&& a: std::initializer_list<std::function<void()>>{
                       []{std::cout << "foo";}, 
                       []{std::cout << "bar";}}) a();
    

我在这里学到的一个技巧是使用回顾性演员表。所以手头有这样一个工具:

template<typename T>
struct memfun_type 
{
    using type = void;
};
template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...) const>
{
    using type = std::function<Ret(Args...)>;
};
template<typename F>
typename memfun_type<decltype(&F::operator())>::type
FFL(F const &func) 
{ // Function from lambda !
    return func;
}

你可以写这样的东西

vector<function<void()>> v = { FFL([]{cout << "foo"; }), FFL([]{cout << "bar"; }) };
for (auto&& a : v) a();

每个 lambda 都是不相关的类型。 碰巧的是,它们都可以转换为 std::function<void()> ,但那是因为std::function会声称可以转换任何东西,并且当它们可以通过void()签名调用并且可复制和可破坏时,它将起作用。

vector情况下,有一个从其构造函数列表中考虑的std::initializer_list<std::function<void()>>构造函数。 这将匹配、尝试和编译。

如果没有该参数(矢量 ctor 的列表参数)匹配,{}语法将改为在其内容中查找通用类型。 没有通用类型,因此失败。

该语言不会搜索每个类型和模板来查找两个(不相关的)lambda 之间可能的通用类型。 它不会读懂你的心思。

你可以做:

 using nullary = std::function<void()>;
 template<class T>
 using il=std::initializer_list<T>;
 for(auto f:il<nullary>{[]{ std::cout<<"hello";},[]{std::cout<<" worldn";}}){
   f();
 }