20

我想做这样的事情:

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

有没有人知道如何解决这个问题或解决这个问题?我正在使用带有 std=c++11 的英特尔编译器 icpc (ICC) 13.1.2

谢谢

4

5 回答 5

33

关于您的问题,有几件事需要澄清。第一个是什么是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)确定参数不能是function类型,因此 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;};

因为 astd::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,在那里(很可能,它还没有被批准)将支持多态 lambdas,它简化了上述类的创建:

auto f = [](auto fn) { fn(0.0); return 0.0; } // unrolls to 'mylambda' above
于 2013-07-15T22:17:22.443 回答
5

尝试使用 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;});
}
于 2013-07-15T21:09:35.263 回答
3

您可能不得不像我们在黑暗时代所做的那样简单地硬着头皮实现自己的函子:

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 之前,这应该可以让您继续前进。

于 2013-07-15T21:59:34.867 回答
1

如果您事先知道 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 */ };
于 2013-07-15T21:12:34.553 回答
0

您可以 cify 捕获 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;}));
}

点击查看工作示例。

于 2017-04-18T07:32:52.520 回答