How to create a variadic generic lambda?

后端 未结 3 1590
礼貌的吻别
礼貌的吻别 2020-12-12 15:02

Since C++14 we can use generic lambdas:

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

This basically means that its call operator is templated

相关标签:
3条回答
  • 2020-12-12 15:38

    Consider this

    #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) This "print" lambda is:

    • Variadic
    • Recursive
    • Generic
    • Fast

    And no templates in sight. (just underneath :) ) No C++ code that looks like radio noise. Simple, clean and most importantly:

    • Easy to maintain

    No wonder "it feels like a new language".

    0 讨论(0)
  • 2020-12-12 15:43

    I am not sure what your intention is but instead of storing it in a std::function you can use the lambda itself to capture the params. This is an example discussed on the boost mailing list. It is used in the boost::hana implementation

    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")); 
    
    0 讨论(0)
  • 2020-12-12 15:47

    Syntax

    How do you create a variadic generic lambda ?

    You can create a variadic generic lambda with the following syntax:

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

    Basically you just add ... between auto (possibly ref qualified) and your parameter pack name.

    So typically using universal references would give:

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

    Usage

    How do you use the parameters ?

    You should consider the variadic generic parameter as having a template parameter pack type, because it is the case. This more or less implies that most if not all usage of those parameters will require templates one way or the other.

    Here is a typical example:

    #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);
    }
    

    Storage

    How do you store a variadic generic lambda ?

    You can either use auto to store a lambda in a variable of its own type, or you can store it in a std::function but you will only be able to call it with the fixed signature you gave to that 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
    

    What about collections of variadic generic lambdas ?

    Since every lambda has a different type you cannot store their direct type in the usual homogeneous containers of the STL. The way it is done with non generic lambdas is to store them in a corresponding std::function which will have a fixed signature call and that won't restrain anything since your lambda is not generic in the first place and can only be invoked that way:

    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);
    

    As explained in the first part of this storage section if you can restrain yourself to a given fixed call signature then you can do the same with variadic generic lambdas.

    If you can't you will need some form of heterogenous container like:

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

    See this question for an example of heterogenous container.

    What else ?

    For more general informations on lambdas and for details on the members generated and how to use the parameters within the lambda see:

    • 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?
    0 讨论(0)
提交回复
热议问题