2

I am trying to store lambda functions in a queue to execute them later. In order to do that, i am trying to hide the parameter pack in a lambda without losing scope so I can still access the passed arguments in an upper scope. Sadly I cannot compile this since the parameter pack does not match the const qualifier. I hope it is easier to understand what I am trying to accomplish by looking at the following code. (I am using c++17, not c++20). I think I misunderstand how to properly forward the variadic parameter pack since as I do it right now, the binding reference to const will discard qualifiers.

It is sadly not an option to expect the parameters in the functional lambda to be const.

std::queue<std::function<void()>> fcts;

template<typename F >
auto push_fct(F &task) -> void {
    // Do things
    fcts.push(std::move(std::function<void()>(task)));
}

template<typename F, typename... A>
auto push_fct(F& task , A&... args) -> void {
    push_fct( [task, args...] { task(args...);});
}

auto main() -> int {

    auto functional = [&](class_a & a, class_b & b) {
        a.memberFct();
        b.memberFunction(123);
    }

    class_a instance_a;
    class_b instance_b;

    push_fct(functional, instance_a, instance_b);


    return 0;
}
Troganda
  • 121
  • 8
  • Important question is, do `instance_a` and `instance_b` exist until the lambda is executed later, or do you want to store copies of them together with the lambda? Or is that what you mean by "*without losing scope*"? – rustyx Mar 16 '21 at 21:02
  • Sorry if I didn't make this clear. The lambda will only be executed if both instances exist and are valid. But the lambda is called in another thread so I'd have to pass them by reference so I can still access the member functions – Troganda Mar 16 '21 at 21:39

1 Answers1

3

What happens is, here [task, args...] the references are lost; task, args... are captured by value, which, in a non-mutable lambda additionally become const, which subsequently can't bind to non-const references in the call to [&](class_a & a, class_b & b).

You can capture the references by reference. Don't worry about their lifetime - the references are collapsed, and the original ones will be captured (source).

template<typename F>
auto push_fct(F&& task) -> void {
    // Do things
    fcts.emplace(std::forward<F>(task));
}

template<typename F, typename... A>
auto push_fct(F& task, A&... args) -> void {
    push_fct([&task, &args...]{ task(args...); });
}
rustyx
  • 80,671
  • 25
  • 200
  • 267