在模板类中使用lambdas

Using lambdas in template class

本文关键字:lambdas      更新时间:2023-10-16

我试图使用一个lambda函数参数的模板类。但是,我不明白如何传递参数。以下是我到目前为止所做的尝试:

#include <iostream>
using namespace std;
template <class F>
class A {
public:
   int f(int i)
   {
       return F(i); //*
   }
};
int main(int argc, const char * argv[]) {
auto f = [](int i){return i+5;};
A<decltype(f)> a;
cout << a.f(5);
return 0;
}

我在标记的行中得到一个错误。

有人能帮忙吗?

你的例子不工作,因为F是一个类型,而不是一个可调用的对象。下一步是通过创建成员变量来实例化它。

template <class F>
class A {
    F function;
public:
    int f(int i) {
         return function(i);
    }
};

但是,这仍然不起作用,因为lambda默认构造函数被删除了。这意味着我们需要另一种方法来构造function。这可以通过向A的构造函数传递一个参数来实现。

template<typename F>
class A {
    F function;
public:
    A(const F& f) : function(f) {}
    int f(int i) {
        return function(i);
    }
};
// ...
auto f = [](int i) { return i+5; };
A<decltype(f)> a(f);

使用lambda复制构造函数,该构造函数不会被删除。

生活例子

如果你想让它和任何lambda一起工作,你可以添加更多的魔法。

template<typename F>
class A {
    F function;
public:
    A(const F& f) : function(f) {}
    template<typename ...Args>
    auto f(Args... args) -> std::result_of_t<F(Args...)> {
        return function(std::forward<Args>(args)...);
    }
};

生活例子

如果你真的想使用模板来接受任何类型的函数的签名,那么实现应该类似于这样:

class A {
 public:
  template<typename F, typename... Args>
  auto f(F&& funct, Args&&... args) {
    return funct(std::forward<Args...>(args)...);
  }
};

那是因为你在评论中说:

问:在类中只需要f方法的F类型吗?
A: 只有方法

正因为如此,当你只需要一个模板方法时,拥有一个模板类应该是无用的。

在这里,一个如何调用方法的例子,它只是调用"可调用对象及其参数",在这种情况下是一个lambda函数:

int main(int argc, char* argv[]) {
  A a;
  a.f([](int i) -> int { return i + 5; }, 12);
  //  |------callable object-----------|   |argument of function|
  return 0;
}

实际上,方法f接受"可调用对象"作为第一个参数,并接受为了调用第一个参数而请求的任何参数作为进一步的参数。


其他说明:

如果你想传递给方法f某种类型的函数签名,例如:int (*)(int),那么你可以避免使用模板而传递std::function类型的对象。

这只是一个例子:

#include <functional>
class A {
 public:
  // method g accept a function which get a integer and return an integer as well.
  int g(std::function<int(int)> funct, int arg) {
    return funct(arg);
  }
};

A定义为继承自或以某种方式包含lambda函数是不够的,当您创建A的实例时,您仍然必须初始化子对象。
要做到这一点,例如,您可以继承lambda并使用A作为可调用对象。
它遵循一个最小的工作示例:

#include <iostream>
using namespace std;
template <class F>
class A: public F {
public:
    A(F f): F(f) {}
};
int main() {
    auto f = [](int i){return i+5;};
    A<decltype(f)> a{f};
    cout << a(5);
    return 0;
}

你不需要定义任何函数f执行 lambda。
无论如何,如果你想让一个函数f被称为a.f(5),你可以这样定义它:

int f(int i) {
    return F::operator()(i);
}

或者如下所示:

int f(int i) {
    return (*this)(i);
}