c++ lambdas how to capture variadic parameter pack from the upper scope

Viewed 16872

I study the generic lambdas, and slightly modified the example, so my lambda should capture the upper lambda's variadic parameter pack. So basically what is given to upper lambda as (auto&&...) - should be somehow captured in [=] block.

(The perfect forwarding is another question, I'm curious is it possible here at all?)

#include <iostream>
#include<type_traits>
#include<utility>


// base case
void doPrint(std::ostream& out) {}

template <typename T, typename... Args>
void doPrint(std::ostream& out, T && t, Args && ... args)
{
    out << t << " ";                // add comma here, see below
    doPrint(out, std::forward<Args&&>(args)...);
}

int main()
{
    // generic lambda, operator() is a template with one parameter
    auto vglambda = [](auto printer) {
        return [=](auto&&... ts) // generic lambda, ts is a parameter pack
        {
            printer(std::forward<decltype(ts)>(ts)...);
            return [=] {  // HOW TO capture the variadic ts to be accessible HERE ↓
                printer(std::forward<decltype(ts)>(ts)...); // ERROR: no matchin function call to forward
            }; // nullary lambda (takes no parameters)
        };
    };
    auto p = vglambda([](auto&&...vars) {
        doPrint(std::cout, std::forward<decltype(vars)>(vars)...);
    });
    auto q = p(1, 'a', 3.14,5); // outputs 1a3.14

    //q(); //use the returned lambda "printer"

}
3 Answers

Instead of using std::tuple and std::apply, which clutter the code a lot, you can use std::bind to bind the variadic arguments to your lambda (for a pre-C++20 solution):

template <typename... Args>
auto f(Args&&... args){

    auto functional = [](auto&&... args) { /* lambda body */ };
    return std::bind(std::move(functional), std::forward<Args>(args)...);
}

The perfect forwarding is another question, I'm curious is it possible here at all?

Well... it seems to me that the perfect forwarding is the question.

The capture of ts... works well and if you change, in the inner lambda,

printer(std::forward<decltype(ts)>(ts)...);

with

printer(ts...);

the program compile.

The problem is that capturing ts... by value (using [=]) they become const values and printer() (that is a lambda that receive auto&&...vars) receive references (& or &&).

You can see the same problem with the following functions

void bar (int &&)
 { }

void foo (int const & i)
 { bar(std::forward<decltype(i)>(i)); }

From clang++ I get

tmp_003-14,gcc,clang.cpp:21:4: error: no matching function for call to 'bar'
 { bar(std::forward<decltype(i)>(i)); }
   ^~~
tmp_003-14,gcc,clang.cpp:17:6: note: candidate function not viable: 1st argument
      ('const int') would lose const qualifier
void bar (int &&)
     ^

Another way to solve your problem is capture the ts... as references (so [&]) instead as values.

Related