Perferct forward a copied std::tuple

262 views Asked by At

I need a bit of help. I need to perfect forward a tuple in a specific way. Imagine this

template <typename F, typename... Args>
auto package_task(F&& func, Args&&... args) -> std::function<void()>
{
    //for the purposes of this example imagine capture to be std::move and
    //to be inplace in the lambdas' capture.
    auto callable = capture(std::forward<F>(func));
    auto params = capture(std::make_tuple(std::forward<Args>(args)...));

    return [callable , params]() mutable { std::apply(callable, params); };
}

the task that is packaged will be executed later on a different thread but when I call apply I need the "callable" to have it's params expanded from the tuple and forwarded exactly like they were passed in the package_task function. I can't use forward_as_tuple since I am copying/moving the arguments and the callable to be executed later. I need something like

template <typename F, typename... Args>
auto package_task(F&& func, Args&&... args) -> std::function<void()>
{
    //for the purposes of this example image capture to be std::move and
    //to be inplace in the lambdas' capture.
    auto callable = capture(std::forward<F>(func));
    auto params = capture(std::make_tuple(std::forward<Args>(args)...));

    return [callable , params]() mutable { std::apply_but_expand_as<Args&&…>(callable, params); };
}

Any ideas would be appreciated.

1

There are 1 answers

3
Barry On BEST ANSWER

If you're trying to make a one-shot callable that forwards its arguments through, the best you can do with C++17 or earlier is:

template <typename F, typename... Args>
auto package_task(F&& func, Args&&... args) -> std::function<void()>
{
    return [func = std::forward<F>(func),
            args_tuple = std::make_tuple(std::forward<Args>(args)...)]
           () mutable
    {
        return std::apply([](auto&&... args){
            return std::invoke(
                std::forward<F>(func),
                std::forward<Args>(args)...
                );
        }, args_tuple);
    };
}

That is, you forward the Args into a tuple, apply the tuple and then forward them back out.


In C++20, thanks to P0780, this can be simplified to:

template <typename F, typename... Args>
auto package_task(F&& func, Args&&... args) -> std::function<void()>
{
    return [func = std::forward<F>(func),
            ...args = std::forward<Args>(args)] () mutable
    {
        return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    };
}