Why use `std::bind_front` over lambdas in C++20?
bind_front
binds the first X parameters, but if the callable calls for more parameters, they get tacked onto the end. This makes bind_front
very readable when you're only binding the first few parameters of a function.
The obvious example would be creating a callable for a member function that is bound to a specific instance:
type *instance = ...;
//lambda
auto func = [instance](auto &&... args) -> decltype(auto) {return instance->function(std::forward<decltype(args)>(args)...);}
//bind
auto func = std::bind_front(&type::function, instance);
The bind_front
version is a lot less noisy. It gets right to the point, having exactly 3 named things: bind_front
, the member function to be called, and the instance on which it will be called. And that's all that our situation calls for: a marker to denote that we're creating a binding of the first parameters of a function, the function to be bound, and the parameter we want to bind. There is no extraneous syntax or other details.
By contrast, the lambda has a lot of stuff we just don't care about at this location. The auto... args
bit, the std::forward
stuff, etc. It's a bit harder to figure out what it's doing, and it's definitely much longer to read.
Note that bind_front
doesn't allow bind
's placeholders at all, so it's not really a replacement. It's more a shorthand for the most useful forms of bind
.
The paper that proposed it Simplified partial function application has some good compelling use cases. I will summarize them here, because otherwise I would have to quote most of the paper, so definitely go check it out:
Automatic perfect forwarding
Using a lambda would involve std::forward
boilerplate
Propagating mutability
In case of storing object by value std::bind
and std::bind_front
propagate constness, but in the case of capturing lambda the user must chose a mutable or const version creating problems
Preserving return type
Using a lambda would involve -> decltype(auto)
boilerplate on the user side.
Preserving value category
Like preserving mutability, except now we are talking about lvalue/rvalue and only std::bind_front
does this correctly
Supporting one-shot invocation
A consequence of propagating mutability and preserving value category
Preserving exception specification
This is especially more important now since exception specification is now part of type system
cppreference has some useful notes as well:
This function is intended to replace std::bind. Unlike std::bind, it does not support arbitrary argument rearrangement and has no special treatment for nested bind-expressions or std::reference_wrappers. On the other hand, it pays attention to the value category of the call wrapper object and propagates exception specification of the underlying call operator.