Why use `std::bind_front` over lambdas in C++20?
Asked Answered
C

2

66

As mentioned in a similarly worded question (Why use bind over lambdas in c++14?) The answer was - no reason (and also mentioned why it would be better to use lambdas).

My question is - if in C++14 there was no longer a reason to use bind, why did the standards committee found it necessary to add std::bind_front in C++20?

Does it now have any new advantage over a lambda?

Cost answered 9/7, 2020 at 5:2 Comment(0)
C
65

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.

Complicated answered 9/7, 2020 at 5:19 Comment(1)
It's worth noting the optimizer benefit that comes from bind_front being that much simpler and more restricted than bind with all its bells and whistles. Much smaller implementation that should be easy for the optimizer to see right through.Illnatured
I
47

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.

Inerasable answered 9/7, 2020 at 5:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.