首页 > 解决方案 > 模板 lambda 与带有模板运算符()的仿函数

问题描述

这个问题的启发,我想将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++14c++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()如果需要来自其“所有者”的许多变量,这可能很乏味。最终,也可以将指针传递thisFunctor::operator(). 示例中不存在这样的复杂情况c++20,其中 lambda 捕获负责捕获所需的变量。

除了简单之外,上述两种方法之间还有其他具体区别吗?效率怎么样?

标签: c++templateslambdavariadic-templatesc++20

解决方案


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

我不同意。

您可以将 lambda 视为一个类/结构,其中一个operator()和一些成员对应于捕获的变量。

所以而不是

[&]<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 等效于带有 constant 的函子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


推荐阅读