如何创建可变参数泛型 lambda

How to create a variadic generic lambda?

本文关键字:变参 参数 泛型 lambda 何创建 创建      更新时间:2023-10-16

从 C++14 开始,我们可以使用通用 lambdas:

auto generic_lambda = [] (auto param) {};

这基本上意味着它的调用运算符是基于标记为 auto 的参数进行模板化的。

问题是如何创建一个可以接受可变参数数的lambda,类似于可变参数函数模板的工作方式?如果这是不可能的,那么可以以同样方式使用的最接近的东西是什么?你会如何存储它?在std::function中可能吗?

我不确定您的意图是什么,但您可以使用lambda本身来捕获参数,而不是将其存储在std::function中。这是在提升邮件列表中讨论的一个例子。它用于 boost::hana 实现

auto list = [](auto ...xs) {
    return [=](auto access) { return access(xs...); };
};
auto head = [](auto xs) {
    return xs([](auto first, auto ...rest) { return first; });
};
auto tail = [](auto xs) {
    return xs([](auto first, auto ...rest) { return list(rest...); });
};
auto length = [](auto xs) {
    return xs([](auto ...z) { return sizeof...(z); });
};
// etc...
// then use it like
auto three = length(list(1, '2', "3")); 

语法

你如何创建一个可变参数泛型 lambda?

您可以使用以下语法创建可变参数泛型 lambda:

auto variadic_generic_lambda = [] (auto... param) {};

基本上,您只需在auto(可能符合 ref 条件(和参数包名称之间添加...

因此,通常使用通用引用会给出:

auto variadic_generic_lambda = [] (auto&&... param) {};

用法

你如何使用参数?

您应该将可变参数泛型参数视为具有模板参数包类型,因为确实如此。这或多或少意味着这些参数的大多数(如果不是全部(用法都需要模板。

下面是一个典型的例子:

#include <iostream>
void print(void)
{
}
template <typename First, typename ...Rest>
void print(const First& first, Rest&&... Args)
{
  std::cout << first << std::endl;
  print(Args...);
}
int     main(void)
{
  auto variadic_generic_lambda = [] (auto... param)
    {
      print(param...);
    };
  variadic_generic_lambda(42, "lol", 4.3);
}

存储

你如何存储可变参数通用λ?

您可以使用 auto 将 lambda 存储在其自身类型的变量中,也可以将其存储在std::function但您只能使用您提供给该std::function的固定签名来调用它:

auto variadic_generic_lambda = [] (auto... param) {};
std::function<void(int, int)> func = variadic_generic_lambda;
func(42, 42); // Compiles
func("lol"); // Doesn't compile

可变参数通用λ的集合呢?

由于每个 lambda 都有不同的类型,因此不能将它们的直接类型存储在 STL 的常用同类容器中。使用非通用 lambda 的方法是将它们存储在相应的std::function中,该将具有固定的签名调用,并且不会限制任何东西,因为您的 lambda 首先不是通用的,只能以这种方式调用:

auto non_generic_lambda_1 = [] (int, char) {};
auto non_generic_lambda_2 = [] (int, char) {};
std::vector<std::function<void(int, char)>> vec;
vec.push_back(non_generic_lambda_1);
vec.push_back(non_generic_lambda_2);

如本存储部分的第一部分所述,如果您可以将自己限制为给定的固定调用签名,那么您可以对可变参数泛型 lambda 执行相同的操作。

如果不能,您将需要某种形式的异构容器,例如:

  • std::vector<boost::variant>
  • std::vector<boost::any>
  • boost::fusion::vector

有关异构容器的示例,请参阅此问题。

还有什么?

有关 lambda 的更多常规信息,以及有关生成的成员以及如何在 lambda 中使用参数的详细信息,请参阅:

  • http://en.cppreference.com/w/cpp/language/lambda
  • 通用 lambda 在 C++14 中如何工作?
  • 如何在所有可变参数模板参数上调用函数?
  • 使用 std::ostream 打印可变参数包的最简单方法是什么?

考虑一下

#include <iostream>
    namespace {
        auto out_ = [] ( const auto & val_) 
        {
            std::cout << val_;
            return out_ ;
        };
        auto print = [](auto first_param, auto... params)
        {
            out_(first_param);
            // if there are  more params
            if constexpr (sizeof...(params) > 0) {
                // recurse
                print(params...);
            }
                return print;
        };
    }
int main()
{
    print("Hello ")("from ")("GCC ")(__VERSION__)(" !"); 
}

(魔杖在这里(这个"打印"λ是:

  • 可变参数
  • 递归的
  • 通用

而且看不到模板。(就在:)下面(没有看起来像无线电噪声的C++代码。简单,干净,最重要的是:

  • 易于维护

难怪"感觉像一门新语言"。