将 lambda 表达式传递给 lambda 参数 c++11

Pass lambda expression to lambda argument c++11

本文关键字:lambda 参数 c++11 表达式      更新时间:2023-10-16

我想做这样的事情:

int main()
{
    auto f = [/*some variables*/](/*take lambda function*/)
    {/*something with lambda function*/};
    f([/*other variables*/](/*variables to be decided by f()*/)
    {/*something with variables*/});
}

我知道可以将 lambda 传递给函数以及 lambda。以下作品:

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;};
    f([](int i) -> double
    {return 0.0;});
}

但是以下内容不起作用(一旦我更改范围变量以添加 [x](

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;}
    f([x](int i) -> double    //[x] does not work
    {return 0.0;});
}

这给出了错误:

error: function "lambda [](double (*)(int))->double::operator()" cannot be called with the given argument list
        argument types are: (lambda [](int)->double)
        object type is: lambda [](double (*)(int))->double

有人会知道如何解决这个问题或解决方法吗?我正在使用英特尔编译器ICPC (ICC( 13.1.2,std=C++11

谢谢

关于您的问题,有几件事需要澄清。第一个是什么是lambda?

lambda 表达式是一个简单的表达式,编译器将从中生成无法命名的唯一类型,同时它将生成该类型的实例。当你编写: [](int i) { std::cout << i; }编译器将为你生成一个大致为:

struct __lambda_unique_name {
   void operator()(int i) const { std::cout << i; }
};

如您所见,它是一个函数,而是一个将operator()实现为 const 成员函数的类型。如果 lambda 执行任何捕获,编译器将生成代码来捕获值/引用。

作为极端情况,对于像上面这样的 lambda,其中没有捕获状态,该语言允许从 lambda 类型转换为带有operator()签名(减去this部分(的函数指针,因此上面的 lambda 可以隐式转换为指向函数的指针,该函数获取int并且不返回任何内容:

void (*f)(int) = [](int i) { std::cout << i; }

既然已经说明了基础知识,那么在您的代码中,您有以下 lambda:

auto f = [x,y](double (func)(int)) -> double {func(0); return 0.0;};
函数

参数的规则(也适用于 lambda(确定参数不能是函数类型,因此 lambda 的参数衰减到指向函数的指针(与数组类型的参数衰减为指针类型的方式相同(:

auto f = [x,y](double (*func)(int)) -> double {func(0); return 0.0;};

稍后,您将尝试传递具有捕获作为参数的 lambda。由于存在捕获,因此特殊规则不适用,并且 lambda 不可转换为指向函数的指针,从而产生您看到的编译器错误。

在当前标准中,您可以采用以下两种方式之一。您可以使用类型擦除从签名中删除可调用实体的确切类型:

auto f = [x,y](std::function<double(int)> func) -> double {func(0); return 0.0;};

由于可以使用具有适当签名的任何可调用实体初始化std::function<double(int)>,因此这将接受以下代码中的 lambda,但代价是类型擦除,这通常意味着动态分配和动态调度。

或者,您可以删除语法糖并手动滚动第一个 lambda 等效项,但使其通用。在这种情况下,如果 lambda 很简单,这可能是一个有效的选项:

struct mylambda {
   template <typename F>
   double operator()(F fn) const {
      fn(0); return 0.0;
   }
} f;
// then use the non-lambda as you tried:
f([x](int i) -> double {return 0.0;});

最后,如果你有足够的耐心,你可以等待 C++14,在那里(很可能,它还没有被批准(将支持多态 lambda,这简化了上述类的创建:

auto f = [](auto fn) { fn(0.0); return 0.0; } // unrolls to 'mylambda' above

尝试使用 std::function:

#include <functional>
int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](std::function<double(int)> func) -> double
             {func(0); return 0.0;};
    f([x](int i) -> double {return 0.0;});
}

您可能必须像我们在黑暗时代所做的那样简单地咬紧牙关并实现自己的函子:

struct F {
    int x;
    int y;
    F(int x_, int y_) : x(x_), y(y_) {}
    template <typename G>
    double operator() (G&& g) const {
        g(0);
        return 0.0;
    }
};
#include <iostream>
int main()
{
    int x = 0;
    int y = 0;
    auto f = F(x, y);
    f([x](int i){return 0.0;});
    f([](int i){std::cout << i << std::endl;});
}

这应该会让你继续前进,直到你的编译器支持 C++14 个通用 lambda。

例如

,如果您事先知道 lambda 的类型,您可以尝试如下操作:

int main()
{
    int x = 0, y = 0;
    auto f = [x]( int i )->double {
        return (double)x;
    };
    auto f2 = [x,y]( decltype(f) func )->double {
        return func( 0 );
    };
    f2( f );
    return 0;
}

或者,您可以使用<functional>库进行更通用的解决方案,例如:

auto f = [x,y]( std::function<double(int)> func ) { /* Do stuff */ };

您可以引用捕获的 lambda,但此解决方案有其局限性:

#include <new>
#include <utility>
namespace
{
template <typename F, int I, typename L, typename R, typename ...A>
inline F cify(L&& l, R (*)(A...) noexcept(noexcept(
  std::declval<F>()(std::declval<A>()...))))
{
  static L l_(std::forward<L>(l));
  static bool full;
  if (full)
  {
    l_.~L();
    new (static_cast<void*>(&l_)) L(std::forward<L>(l));
  }
  else
  {
    full = true;
  }
  return [](A... args) noexcept(noexcept(
      std::declval<F>()(std::forward<A>(args)...))) -> R
    {
      return l_(std::forward<A>(args)...);
    };
}
}
template <typename F, int I = 0, typename L>
inline F cify(L&& l)
{
  return cify<F, I>(std::forward<L>(l), F());
}

int main()
{
    int x=0;
    int y=0;
    auto f = [x,y](double (func)(int)) -> double
    {func(0); return 0.0;};
    f(cify<double(*)(int i)>([x](int i) -> double    //works now
    {return 0.0;}));
}

单击查看工作示例。