Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation of std::function

Tags:

c++

c++11

What is the purpose of std::function? As far as I understand, std::function turns a function, functor, or lambda into a function object.

I don't quite understand the purpose of this... Both Lambdas and Functors are function objects already and I do believe that they can be used as predicates for algorithms like sort and transform. As a side note, Lambdas are actually Functors (internally). So the only thing I can see std::function being useful for is to turn regular functions into function objects.

And I don't quite see why I would want to turn a regular function into a function object either. If I wanted to use a function object I would have made one in the first place as a functor or lambda... rather than code a function and then convert it with std::function and then pass it in as predicate...

I'm guessing that there is much more to std::function... something that isn't quite obvious at first glance.

An explanation of std::function would be much appreciated.

like image 628
code Avatar asked Aug 20 '18 16:08

code


People also ask

What is STD function used for C++?

std::function is a type erasure object. That means it erases the details of how some operations happen, and provides a uniform run time interface to them. For std::function , the primary1 operations are copy/move, destruction, and 'invocation' with operator() -- the 'function like call operator'.

Should std :: function be passed by value?

If there is any possibility you are storing a copy of the std::function , pass by value. Otherwise, either way is roughly equivalent: the only downside to by-value is if you are taking the same bulky std::function and having one sub method after another use it. Barring that, a move will be as efficient as a const& .

Does std :: function allocate?

As it happens, std::function is guaranteed to not allocate if constructed from a function pointer, which is also one word in size. So constructing a std::function from this kind of lambda, which only needs to capture a pointer to an object and should also be one word, should in practice never allocate.

Is a lambda a std :: function?

Lambda's type One important thing to note is that a lambda is not a std::function . It is true that a lambda can be assigned to a std::function , but that is not its native type.


4 Answers

What is the purpose of std::function? As far as I understand, std::function turns a function, functor, or lambda into a function object.

std::function is an example of a broader concept called Type Erasure. The description you have isn't quite accurate. What std::function<void()> does, to pick a specific specialization, is represent any callable that can be invoked with no arguments. It could be a function pointer or a function object that has a concrete type, or a closure built from a lambda. It doesn't matter what the source type is, as long as it fits the contract - it just works. Instead of using the concrete source type, we "erase" it - and we just deal with std::function.

Now, why would we ever use type erasure? After all, don't we have templates so that we can use the concrete types directly? And wouldn't that be more efficient and isn't C++ all about efficiency?!

Sometimes, you cannot use the concrete types. An example that might be more familiar is regular object-oriented polymorphism. Why would we ever store a Base* when we could instead store a Derived*? Well, maybe we can't store a Derived*. Maybe we have lots of different Derived*s that different users use. Maybe we're writing a library that doesn't even know about Derived. This is also type erasure, just a different technique for it than the one std::function uses.

A non-exhaust list of use-cases:

  • Need to store a potentially heterogenous list of objects, when we only care about them satisfying a concrete interface. For std::function, maybe I just have a std::vector<std::function<void()>> callbacks - which might all have different concrete types, but I don't care, I just need to call them.
  • Need to use across an API boundary (e.g. I can have a virtual function taking a std::function<void()>, but I can't have a virtual function template).
  • Returning from a factory function - we just need some object that satisfies some concept, we don't need a concrete thing (again, quite common in OO polymorphism, which is also type erasure).
  • Could potentially actually use templates everywhere, but the performance gain isn't worth the compilation hit.
like image 92
Barry Avatar answered Oct 14 '22 01:10

Barry


Consider a simple use case:

/* Unspecified */ f = [](int x, int y){ return x + y; };
f = [](int x, int y){ return x - y; };
int a = 42;
f = [&a](int x, int y){ return a * x * y; };

How would you specify /* Unspecified */?

Furthermore,

std::queue<of what?> jobs;
jobs.push_back([]{ std::cout << "Hi!\n"; });
jobs.push_back([]{ std::cout << "Bye!\n"; });
for(auto const &j: jobs) j();

What value_type should be kept in jobs?

Finally,

myButton.onClick(f);

What type does f have? A template parameter? Okay, but how is it registered internally?

like image 32
bipll Avatar answered Oct 14 '22 01:10

bipll


In most uses that I've seen, std::function was overkill. But it serves two purposes.

First, it gives you a uniform syntax for calling function objects. For example, you can use an std::function instantiation to wrap an ordinary function that takes a single argument of a class type or a member function and the class object that it should be applied to without worrying about the different calling syntax.

struct S {
    void f();
};

void g(const S&);

S obj;

typedef std::function<void()> functor1(&S::f, obj);
typedef std::function<void()> functor2(&g, obj);

functor1(); // calls obj.f()
functor2(); // calls g(obj);

Note that both functors here are called with the same syntax. That's a big benefit when you're writing generic code. The decision of how to call the underlying function is made within the std::function template, and you don't have to figure it out in your code.

The other big benefit is that you can reassign the function object that a std::function object holds:

functor1 = std::function<void>()>(&g, obj);

This changes the behavior of functor1:

functor1() // calls g(obj)

Sometimes that matters.

like image 2
Pete Becker Avatar answered Oct 14 '22 00:10

Pete Becker


As far as I understand, std::function turns a function, functor, or lambda into a function object.

You pretty much summed it up, you can turn any of these into the same thing, an std::function, that you can then store and use as you wish.

When you are designing a class or an API in general you usually don't have a reason to restrict your features to just one of these, so using std::function gives the liberty of choice to the user of your API, as opposed to forcing users to one specific type. You can even store different forms of these together, it's basically an abstraction of callable types with a given signature and a clearly defined semantic.

like image 1
Drax Avatar answered Oct 14 '22 00:10

Drax