Explanation of std::function

Explanation of std::function



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


std::function



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.


std::function



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.


std::function



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


std::function





Because you cannot create a vector of lambdas.
– Axalo
Aug 20 at 16:27





It's required whenever you need to erase the type of the function object, vector of lambdas mentioned in the previous comment being one such example.
– Praetorian
Aug 20 at 16:35





An observer pattern object is an example of where it's very useful to do type erasure - for example, if you need to store a "callback" or a vector of "callbacks" to be called later when some condition triggers.
– Daniel Schepler
Aug 20 at 16:40





@Praetorian the case when "more is less". Type erasure is most the basic and most forgotten/ignored feature of OOP.
– Swift - Friday Pie
Aug 20 at 16:41





Thanks everyone for the clear explanations!
– code
Aug 20 at 19:18




6 Answers
6



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


std::function



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.


std::function


std::function<void()>


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.


Base*


Derived*


Derived*


Derived*


Derived


std::function



A non-exhaust list of use-cases:


std::function


std::vector<std::function<void()>> callbacks


virtual


std::function<void()>


virtual



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 */?


/* 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?


value_type


jobs



Finally,


myButton.onClick(f);



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


f



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.


std::function



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.


std::function



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


std::function



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.


std::function


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.


std::function



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


std::function


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



This changes the behavior of functor1:


functor1


functor1() // calls g(obj)



Sometimes that matters.



One example of where std::function can be very useful is in implementing an "observer pattern". So, for example, say you want to implement a simple "expression evaluator" calculator GUI. To give a somewhat abstract idea of the kind of code you might write against a GUI library using the observer pattern:


std::function


class ExprEvalForm : public GuiEditorGenerated::ExprEvalForm
public:
ExprEvalForm()
calculateButton.onClicked(
auto exprStr = exprInputBox.get();
auto value = ExprEvaluator::evaluate(exprStr);
evalOutputLabel.set(std::to_string(value));
);

;



Now, how would the GUI library's button class store the function that's passed to onClicked? Here, an onClicked method (even if it were templated) would still need to store somewhere into a member variable, which needs to be of a predetermined type. That's exactly where the type erasure of std::function can come into play. So, a skeleton of the button class implementation might look like:


onClicked


onClicked


std::function


class PushButton : public Widget
public:
using ButtonClickedCallback = std::function<void()>;
void onClicked(ButtonClickedCallback cb)
m_buttonClickedCallback = std::move(cb);


protected:
void mouseUpEvent(int x, int y) override
...
if (mouseWasInButtonArea(x, y))
notifyClicked();
...


private:
void notifyClicked()
if (m_buttonClickedCallback)
m_buttonClickedCallback();

ButtonClickedCallback m_buttonClickedCallback;
;



Using function object is helpful when implementing thread pool. You can keep no of available workers as threads and work to do as queue of function objects. It is easier to keep work to be done as function object than function pointers for example as you can just pass anything thats callable. Each time new function object appear in queue, worker thread can just pop it and execute by calling () operator on it.






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

ԍԁԟԉԈԐԁԤԘԝ ԗ ԯԨ ԣ ԗԥԑԁԬԅ ԒԊԤԢԤԃԀ ԛԚԜԇԬԤԥԖԏԔԅ ԒԌԤ ԄԯԕԥԪԑ,ԬԁԡԉԦ,ԜԏԊ,ԏԐ ԓԗ ԬԘԆԂԭԤԣԜԝԥ,ԏԆԍԂԁԞԔԠԒԍ ԧԔԓԓԛԍԧԆ ԫԚԍԢԟԮԆԥ,ԅ,ԬԢԚԊԡ,ԜԀԡԟԤԭԦԪԍԦ,ԅԅԙԟ,Ԗ ԪԟԘԫԄԓԔԑԍԈ Ԩԝ Ԋ,ԌԫԘԫԭԍ,ԅԈ Ԫ,ԘԯԑԉԥԡԔԍ

How to change the default border color of fbox? [duplicate]

Henj