这(简单)lambda函数有什么问题?

时间:2016-03-18 05:58:18

标签: c++ c++11 lambda

捕获的变量有问题,但我无法弄清楚是什么。我得到的错误是

error: no matching function for call to ‘applyFunc(int, main()::<lambda(int)>)’
  applyFunc<int,int>(0,[=](int z) -> int{return z + xx;}); //error!

对错误有任何见解?

//given input x and function func, return func(x)
template <typename T, typename U>
U applyFunc( T x, U func(T) )
{
    return func(x);
}

int main()
{
    int xx = 2;
    applyFunc<int,int>(0,[](int x) -> int {return x + 1;}); //no error
    applyFunc<int,int>(0,[=](int z) -> int{return z + xx;}); //error!

    return 0;
}

编辑:使用&lt;中的函数对象进行修复功能性&gt;。

#include <functional>

template <typename RET, typename INPUT>
RET applyFunc( INPUT x, std::function<RET(INPUT)> func )
{
    return func(x);
}

int aFunc(int x)
{
    return x;
}

int main()
{
    int xx = 4;
    applyFunc<int,int>(0,[](int x) -> int {return x + 1;});
    applyFunc<int,int>(0,[=](int z) -> int{return z + xx;});

    return 0;
}

3 个答案:

答案 0 :(得分:6)

您的参数func被声明为常规函数指针。 Lambdas不是常规功能。然而lambdas是 convertible 到常规函数指针,但前提是它们都有空捕获子句。

Lambda

[](int x) -> int {return x + 1;}

具有空捕获子句,这使得它可以转换为函数指针类型int (*)(int)

Lambda

[=](int z) -> int{return z + xx;}

具有非空捕获子句,这意味着它不能转换为常规函数指针。

您使用std::function的想法将使您的代码编译和工作。然而,这种方法存在固有的低效率。 std::function是基于运行时多态性的类型擦除技术的实现。其主要目的之一是促进从编译时多态性切换到运行时多态性,从而减少&#34;模板化&#34;的代码。基本上,通过使用std::function,您同意隐藏在std::function实现中的运行时开销,以减少模板引起的代码膨胀。

此问题不适用于您的特定代码。您的模板函数很小,并且在每个方面都依赖于编译时参数。你的函数纯粹是概念性的,应该在结果代码中完全消解(消失)。在这种情况下,你想要它通过编译时多态。引入std::function只是为了使它编译看起来不是正确的想法。这是用大炮射击麻雀并支付头顶费用。

你可以做什么(作为第一步)只是为仿函数类型添加另一个模板参数

template <typename T, typename U, typename F>
U applyFunc( T x, F func )
{
    return func(x);
}

作为第二步,您立即注意到模板参数U现在过多:它由TF的组合隐式引入。通过从F中提取此类型,您可以将模板重写为

template <typename T, typename F>
std::result_of_t<F(T)> applyFunc( T x, F func )
{
    return func(x);
}

(这可以通过使用&#34;完美转发&#34;语言功能进一步改进。)

请注意,这也可以使编译器完全扣除函数模板参数,因此您不再需要明确指定它们

int xx = 2;
applyFunc(0,[](int x) { return x + 1; });
applyFunc(0,[=](int z) { return z + xx; });

我们还可以注意到,我们甚至可以尝试摆脱T作为模板参数(从F中提取),但我不会这样做。保持T灵活性使模板支持函数重载的自然概念。

答案 1 :(得分:3)

我不能对markyxl回答发表评论,所以我会提供另一个回答。您可以使用自动 decltype 来代替指定返回类型:

#include <iostream>
using namespace std;

template <typename T, typename U>
auto applyFunc(T x, U func) -> decltype(func(x))
{
  return func(x);
}

int main()
{
  int xx = 2;
  bool a = applyFunc<bool>(0,[](int x) -> bool {return x > 0;});
  bool b = applyFunc<bool>(0,[=](int z) -> bool {return z + xx > 0;});

  cout << "a = " << a << " b = " << b << endl;
}

答案 2 :(得分:1)

以下是可行的,唯一需要注意的是你必须手动指定lambda函数的返回类型:

//given input x and function func, return func(x)
template <typename RET, typename T, typename U>
RET applyFunc(T x, U func)
{
  return func(x);
}

int main()
{
  int xx = 2;
  bool a = applyFunc<bool>(0,[](int x) -> bool {return x > 0;});
  bool b = applyFunc<bool>(0,[=](int z) -> bool {return z + xx > 0;});

  return 0;
}