带有模板operator()的模板lambda与函子

时间:2019-07-03 08:18:15

标签: c++ templates lambda variadic-templates c++20

this question的启发,我想将c++20模板lambda与具有模板operator()的函子的用法进行比较。

作为测试用例,请考虑一个模板函数call,该函数以模板lambda作为参数,并调用此lambda以一些模板参数实例化它。以下c++20代码举例说明了这个想法。

#include <tuple>
#include <utility>

template <int I, class Lambda, class... ArgsType>
void call(Lambda&& F, ArgsType&& ...args)
{
           F.template operator()<I>(std::forward<ArgsType>(args)...);
}

int main() {
   std::tuple<int, double, int> t{0,0,0};
   int a = 2;

   auto f = [&]<int I>(auto& x) { std::get<I>(x) += I + a; };
   call<0>(f, t);

   return 0;
}

c++11 / c++14 / c++17中,没有模板lambda,可以使用具有模板operator()的仿函数实现相同的任务,如下所示代码。

#include <tuple>
#include <utility>

template <int I, class Lambda, class... ArgsType>
void call(Lambda&& F, ArgsType&& ...args)
{
           F.template operator()<I>(std::forward<ArgsType>(args)...);
}

struct Functor {
    template <int I, class T>
    void operator()(const int& a, T& x) { std::get<I>(x) += I + a; };
};

int main() {
   std::tuple<int, double, int> t{0,0,0};
   int a = 2;

   Functor func{};
   call<0>(func, a, t);

}

在第二个示例中看到的主要缺点是,为了模拟lambda捕获,需要将所有局部变量(在这种情况下为int a)显式传递给函子。如果Functor::operator()需要其“所有者”中的许多变量,这可能很麻烦。最终,也许还可以将指针this传递到Functor::operator()c++20示例中没有这种复杂性,其中lambda捕获负责捕获所需的变量。

除了简单之外,上述两种方法之间是否还有其他具体区别?效率如何?

1 个答案:

答案 0 :(得分:2)

  

在第二个示例中看到的主要缺点是,要模拟lambda捕获,需要将所有局部变量(在本例中为ina a)显式传递给函子。

我不同意。

您几乎可以看到带有一个operator()和一些与捕获的变量相对应的成员的lambda类/结构。

所以不是

[&]<int I>(auto& x) { std::get<I>(x) += I + a; };

您可以编写(已经从C ++ 11开始)

struct nal // not a lambda
 {
   int const & a;

   template <int I, typename T>
   auto operator() (T & x) const
    { std::get<I>(x) += I + a; }
 };

并按以下方式使用

call<0>(nal{a}, t);

我对函子看到的主要缺点(并且我认为这是一个缺点是可疑的)是您不能简单地通过引用或值([&][=])捕获所有外部变量,但您必须显式列出您使用的所有变量,包括定义函子和初始化函子对象。

主题外:观察到我在const结构中标记了operator() nal

如果不修改捕获的变量,则lambda等效于常数为operator()的函子。

这很重要,如果您将函子作为常量引用传递

template <int I, class Lambda, class... ArgsType>
void call (Lambda const & F, ArgsType&& ...args)
{ // ......^^^^^^^^^^^^^^  now F is a constant reference
  F.template operator()<I>(std::forward<ArgsType>(args)...); // compilation error
                                                             // if operator()
                                                             // isn't const
} 

如果operator()不是const,则会出现编译错误