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

Posted

技术标签:

【中文标题】如何创建可变参数泛型 lambda?【英文标题】:How to create a variadic generic lambda? 【发布时间】:2014-11-11 05:07:42 【问题描述】:

从 C++14 开始,我们可以使用泛型 lambda:

auto generic_lambda = [] (auto param) ;

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

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

【问题讨论】:

[](auto... params) ... ? @Xeo 不错!你将如何使用它?您可以将其存储在 std::function 中吗? 不,因为std::function 的调用签名并非必然是多态的。 使用示例:weird lambda @GingerPlusPlus GCC 4.9 and Clang 3.5 【参考方案1】:

我不确定您的意图是什么,但您可以使用 lambda 本身来捕获参数,而不是将其存储在 std::function 中。 这是在 boost 邮件列表中讨论的示例。它用于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")); 

【讨论】:

【参考方案2】:

语法

如何创建可变参数泛型 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);

存储

如何存储可变参数泛型 lambda?

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

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 集合怎么样?

由于每个 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&lt;boost::variant&gt; std::vector&lt;boost::any&gt; boost::fusion::vector

查看this question 了解异构容器的示例。

还有什么?

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

http://en.cppreference.com/w/cpp/language/lambda How does generic lambda work in C++14? How to call a function on all variadic template args? What is the easiest way to print a variadic parameter pack using std::ostream?

【讨论】:

@Mikhail Basically you just add ... between auto (possibly ref qualified) and your parameter pack name. 因此,通常在具有通用引用参数包的情况下,您将拥有[](auto&amp;&amp;... params) 。不确定是否值得将此特定示例添加到帖子中。 这就是我在找到您的帖子时所寻找的。也许,我不是唯一一个。谢谢。 能否请您展示一个在 lambda 中使用参数的示例 - 您在此处编写的许多示例,但没有人给出完整示例 @serup 您使用参数的方式与使用模板参数包的方式相同,其他问题(***.com/q/17339789/1147772)涵盖了这一点,我会将链接添加到what else 部分 @Drax,此示例不显示 lambda 函数,仅显示模板函数【参考方案3】:

考虑一下

#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__)(" !"); 

(wandbox here) 这个“打印” lambda 是:

可变参数 递归 通用 快速

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

易于维护

难怪“感觉就像一门新语言”。

【讨论】:

为了不混淆答案,我故意只说“快”。它实际上非常快。因为它几乎是一个编译时代码。因此,if-constexpr 优化是可能的。 这也是为什么我没有使用正确的签名 auto print = [](auto &amp;&amp; first_param, auto &amp;&amp; ... params) 它在魔杖盒示例中答案中提供 @Nikos,我不明白你为什么认为它不是递归的? “打印” lambda 调用自身。这是编译时递归的,任何好的编译器都会将其转换为非递归的以供运行时使用。是这个意思吗? 嗨@Elliot,“Nikos”是谁? @ChefGladiator 老实说我不记得这段对话了,但我猜一些“Nikos”删除了他们的评论......

以上是关于如何创建可变参数泛型 lambda?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写一个丢弃其参数的通用可变参数 lambda?

c ++ lambdas如何从上层范围捕获可变参数包

是否可以解决“为可变参数参数创建 T 的通用数组”编译器警告?

Kotlin泛型 ② ( 可变参数 vararg 关键字与泛型结合使用 | 使用 [] 运算符获取指定可变参数对象 )

Python 3 类型,具有任意数量的包含类型的自定义可变参数泛型,如何?

JAVA中,关于可变参数和泛型的问题。