【问题标题】:Pass lambda expression to lambda argument c++11将 lambda 表达式传递给 lambda 参数 c++11
【发布时间】:2013-07-15 20:53:28
【问题描述】:

我想做这样的事情:

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 不能转换为函数指针
  • 为什么不是constexprauto 无论如何都是编译时特性...
  • @AndyProwl 什么? 10 分钟内没有您的详细答复?来吧,你让我们在这里工作太辛苦了,你甚至没有一天的代表;-)
  • 您的代码中没有语法错误吗?在f([x](int i) -> double 行中,括号没有闭合。
  • @TemplateRex:这些天我有点忙于回答自己的问题:-D

标签: c++ c++11 lambda icc


【解决方案1】:

关于您的问题,有几件事需要澄清。第一个是什么是 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() 签名的函数指针(减去 @987654327 @ 部分),因此上面的 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;};

因为std::function&lt;double(int)&gt; 可以使用任何具有适当签名的可调用 实体进行初始化,所以这将接受下面代码中的 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

【讨论】:

  • 从我读过的内容以及 [this comment] (stackoverflow.com/questions/7951377/…) 来看,std::function 的机制比您展示的模板技术慢,尽管有人说这是由于虚拟调用,而您说这是由于动态分配(尽管您也说动态调度,我认为这是virtual 的同义词)。一般来说,您会推荐使用模板?
【解决方案2】:

尝试使用 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;});
}

【讨论】:

  • 感谢您的快速回复。我尝试过使用它,但英特尔编译器不支持它,如here 所示。 boost/function 库可能是一个潜在的替代方案,但我想尽量避免导入它。有什么替代方案?
  • 升级/切换你的编译器。您不能只将变量捕获到原始函数指针中,您需要一个对象。如果您的编译器提供了 lambdas 但无法存储它们(这是 std::function 的主要目的)恕我直言,您的编译器实际上并不支持 lambdas。
  • hmmm... 是的,我应该只是想办法绕过 lambdas :(
【解决方案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 之前,您应该可以继续前进。

【讨论】:

  • 在那里使用右值引用的目的或好处是什么? (即(G&amp;&amp; g)(G g) 好多少?
  • @ChrisBecke 通过转发引用而不转发来接受参数是惯用的,因为“我想可能改变这个参数并忽略值类别。”
【解决方案4】:

如果您事先知道 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;
}

或者您可以使用&lt;functional&gt; 库获得更通用的解决方案,例如:

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

【讨论】:

    【解决方案5】:

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

    Click 是一个工作示例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-21
      • 2012-12-27
      • 2022-01-19
      • 1970-01-01
      相关资源
      最近更新 更多