简体   繁体   中英

How to use std::function using variadic template

There is a template for constructing std::function :

template< class R, class... Args >
class function<R(Args...)>;

I have not figured out how to invoke it. (VC++, if that matters.)

Question: How can I use std::function with a variadic list without using std::bind ?

#include <functional>
#include <iostream>
using vfunc = std::function<void()>;
using namespace std; // I know, I know.

template<class F, class... Args>
void
run(F&& f, Args&&... args) {
    vfunc fn = bind(forward<F>(f), forward<Args>(args)...); //OK
    //vfunc fn = vfunc(forward<F>(f), forward<Args>(args)...); // COMPILER ERROR
    fn();
}

void foo(int x) {
    cout << x << " skidoo\n";
}
int main() {
    run(foo, 23);
    return 0;
}

There is a template for constructing std::function .

 template< class R, class... Args > class function<R(Args...)>; 

That's not what that declaration means. It's not declaring a constructor or a "template for constructing" anything; it's a specialization for the template class std::function . The specialization is the only definition of std::function; the base template is never defined. I think the point of this has something to do with using a function signature in the template declaration. That is, being able to use the template via function rather than as function.

You want to take a callable object and some number of values and create a new callable object that stores those values, which has an operator() overload that calls the given function with those values. std::function does not do that; it has no constructors for doing so.

This is exactly what std::bind is for. Basically, your code is fine as is: storing the result of bind within function is entirely valid.

In that case, you can just wrap the function in a lambda and construct your std::function from it.

template<class F, class... Args>
void run(F&& f, Args&&... args) {
    auto fn = vfunc{
        [=]() mutable {
            std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
        }
    };

    fn(); // fn is of type std::function<void()>
}

I made the lambda mutable so std::forward will not silently not move.

Note however that the [=] capture will copy everything. To support move only types, you can use a tuple:

[f = std::forward<F>(f), args = std::tuple{std::forward<Args>(args)...}]() mutable {
    std::apply(std::forward<F>(f), std::move(args));
}

In C++20, this becomes easier:

[f = std::forward<F>(f), ...args = std::forward<Args>(args)...]() mutable {
    std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM