Why Use Std::Bind Over Lambdas in C++14

Why use std::bind over lambdas in C++14?

Scott Meyers gave a talk about this. This is what I remember:

In C++14 there is nothing useful bind can do that can't also be done with lambdas.

In C++11 however there are some things that can't be done with lambdas:

  1. You can't move the variables while capturing when creating the lambdas. Variables are always captured as lvalues. For bind you can write:

    auto f1 = std::bind(f, 42, _1, std::move(v));
  2. Expressions can't be captured, only identifiers can. For bind you can write:

    auto f1 = std::bind(f, 42, _1, a + b);
  3. Overloading arguments for function objects. This was already mentioned in the question.

  4. Impossible to perfect-forward arguments

In C++14 all of these possible.

  1. Move example:

    auto f1 = [v = std::move(v)](auto arg) { f(42, arg, std::move(v)); };
  2. Expression example:

    auto f1 = [sum = a + b](auto arg) { f(42, arg, sum); };
  3. See question

  4. Perfect forwarding: You can write

    auto f1 = [=](auto&& arg) { f(42, std::forward<decltype(arg)>(arg)); };

Some disadvantages of bind:

  • Bind binds by name and as a result if you have multiple functions with the same name (overloaded functions) bind doesn't know which one to use. The following example won't compile, while lambdas wouldn't have a problem with it:

    void f(int); void f(char); auto f1 = std::bind(f, _1, 42);
  • When using bind functions are less likely to be inlined

On the other hand lambdas might theoretically generate more template code than bind. Since for each lambda you get a unique type. For bind it is only when you have different argument types and a different function (I guess that in practice however it doesn't happen very often that you bind several time with the same arguments and function).

What Jonathan Wakely mentioned in his answer is actually one more reason not to use bind. I can't see why you would want to silently ignore arguments.

Bind Vs Lambda?

As you said, bind and lambdas don't quite exactly aim at the same goal.

For instance, for using and composing STL algorithms, lambdas are clear winners, IMHO.

To illustrate, I remember a really funny answer, here on stack overflow, where someone asked for ideas of hex magic numbers, (like 0xDEADBEEF, 0xCAFEBABE, 0xDEADDEAD etc.) and was told that if he were a real C++ programmer he would simply have download a list of English words and use a simple one-liner of C++ :)

#include <iterator>
#include <string>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

int main()
{
using namespace boost::lambda;
std::ifstream ifs("wordsEn.txt");
std::remove_copy_if(
std::istream_iterator<std::string>(ifs),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"),
bind(&std::string::size, _1) != 8u
||
bind(
static_cast<std::string::size_type (std::string::*)(const char*, std::string::size_type) const>(
&std::string::find_first_not_of
),
_1,
"abcdef",
0u
) != std::string::npos
);
}

This snippet, in pure C++98, open the English words file, scan each word and print only those of length 8 with 'a', 'b', 'c', 'd', 'e' or 'f' letters.

Now, turn on C++0X and lambda :

#include <iterator>
#include <string>
#include <algorithm>
#include <iostream>
#include <fstream>

int main()
{
std::ifstream ifs("wordsEn.txt");
std::copy_if(
std::istream_iterator<std::string>(ifs),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"),
[](const std::string& s)
{
return (s.size() == 8 &&
s.find_first_not_of("abcdef") == std::string::npos);
}
);
}

This is still a bit heavy to read (mainly because of the istream_iterator business), but a lot simpler than the bind version :)

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.

Is std::bind still useful compared to lambdas?

You can capture by value or by reference, and the problem is that capture by value really means 'capture by copy'. This is a show stopper for a move-only type. So you can't use a lambda to do the following:

struct foo { void bar() {} };

std::unique_ptr<foo> f { new foo };
auto bound = std::bind(&foo::bar, std::move(f));
static_assert( std::is_move_constructible<decltype(bound)>::value, "" );
bound();

IIRC the Standard Committee briefly considered allowing arbitrary expression inside a lambda capture list to solve this (which could look like [std::move(f)] { return f.bar(); }), but I don't think there was a solid proposal and C++11 was already running late.

That and the restriction to monomorphic behaviour with lambdas are the deal breakers for me.

Bind or Lambda for member function callback c++14

This:

template<class T>
void addCallBack(T* instance) {
func = [&instance](int x) {
instance->someFunc(x);
}
}

is capturing the argument instance by reference, which goes out of scope at the end of addCallBack() so you end up with a dangling reference. So, definitely not that.

What you want to do is:

func = [instance](int x){ instance->someFunc(x); }

or just:

func = [=](int x){ instance->someFunc(x); }

Now there isn't going to be a functional difference between that and:

func = std::bind(&T::someFunc, instance, std::placeholders::_1);

but the lambda is going to be typically easier to read (as it is in this case), and easier to inline, and is more capable of doing arbitrary complex things. The latter two don't matter in this case - but are good reasons to basically always prefer lambdas.


Of course, this doesn't work if someFunc is an overloaded name, which is one more reason to prefer a lambda - which will always work.

Must I use lambda to pass a member function as std::function?

std::bind is the classical approach, that avoids the need to explicitly spell out the forwarding signature:

using namespace std::placeholders;

// ...

worker(std::bind(&Caller::receiver, this, _1, _2, _3));

C++20 also has std::bind_front; it reduces this verbiage, somewhat.

You cannot pass a pointer to a member function "directly". This is because a member function requires a specific instance of an object whose member function should get invoked. So, in some form of fashion, in some way, you cannot avoid this, or some other instance of the object, to be involved in the process. This is fundamental to C++. The only thing that can be done here is to find some syntactic sugar, which is basically all this is, here.

std::function works beautifully with std::bind - but why?

Q: What are the basic rules that effectively determine: dist(rng) being used - I don't see why std::bind would enforce this interaction. A lot of interactions seem based around operator () methods.

std::bind performs function composition. The first argument must be a function object, i.e. something callable like a function (e.g. a normal function, or a class with an overloaded operator()).

A call to std::bind makes copies of its arguments, "binds" the copies of the arguments to the first argument (the function object), and returns a new function object that will invoke the copy of the function object.

So in a simple case:

int f(int i) { return i; }
auto f1 = std::bind(f, 1);

this binds the value 1 to the function f, creating a new function object that can be called with no arguments. When you invoke f1() it will invoke f with the argument 1, i.e. it will call f(1), and return whatever that returns (which in this case is just 1).

The actual type of the thing returned by std::bind(f, 1) is some implementation-specific class type, maybe called something like std::__detail::__bind_type<void(*)(int), int>. You're not meant to refer to that type directly, you would either capture the object using auto or store it in something else that doesn't care about the precise type, so either:

auto f1 = std::bind(f, 1);

or:

std::function<int()> f1 = std::bind(f, 1);

In your more complex case, when you call std::bind(decltype(p_dist){0, p - 1}, std::ref(rng))) you get a new function object that contains a copy of the temporary decltype(p_dist){0, p - 1} and a copy of the reference_wrapper<std::mt19937> created by std::ref(rng). When you invoke that new function object it will call the contained distribution, passing it a reference to rng.

That means it is callable with no arguments, and it will call the contained random number distribution with the contained random engine, and return the result.

Q: std::function is helpfully referred to as 'a general-purpose polymorphic function wrapper' on cppreference.com. So is it a function that encapsulates a uint64_t return type?

A std::function<uint64_t()> is a wrapper for a function object that is callable with no arguments and that returns a uint64_t (or something implicitly convertible to uint64_t). It can be used to store a copy of an arbitrary function object that is copy constructible, and callable with no arguments, and returns something convertible to uint64_t.

(And more generally, a std::function<R(A1, A2 ... AN)> is a wrapper for a function object that returns R when called with N arguments, of types A1, A2 ... AN.)

Since the result of your std::bind call is a copy constructible function object that is callable with no arguments and returns a uint64_t, you can store the result of that std::bind call in a std::function<uint64_t()>, and when you invoke the function it will invoke the result of the bind call, which will invoke the contained distribution, and return the result.

Or again, making use of operator () syntax to drive the notion of a function?

I'm not sure what this means, but in general it's true that in C++ we often talk about "function objects" or "callables" which are generalisations of functions, i.e. something that can be invoked using function call syntax, e.g. a(b, c)

Problem using std::transform with lambdas VS std::transform with std::bind

The function argument to std::bind or std::bind_front should not be invoked, but rather the function itself is passed as the first argument:

using T = std::iter_value_t<InputIterator>;
using F = T (*)(T, T, T) noexcept;

return std::transform(first, last, result,
std::bind_front<F>(std::lerp, a, b));

Godbolt

Passing result of std::bind to std::function overloads

Stop using std::bind. It is a mess of random features and quirks.

Todays quirk is that std::bind will accept an unlimited number of arguments and discard any extra ones. Tomorrow you might run into the fact that passing std::bind result to std::bind does strange magic.

std::bind was ported over to boost at the same time lambdas where added to the language. Lambdas solve almost every problem bind does in just as clear syntax and fails to have the myraid of quirks bind does, especially post C++14 when auto lambdas are available. (Most C++11 compilers also supported auto lambda).

You can write functions so that one or the other is the preferred overload when they both apply. But doing so adds a pile of noise to your interface, and in this case about the only reason why you'd want that preference is because std::bind is doing something stupid.

Engineering around a poorly designed bit of std library is not worth it. Simply stop using that poorly designed bit of std library, or at point of use cast explicitly.


Failing that, do this:

template <class T, class F,
std::enable_if_t<
std::is_convertible<
std::result_of_t<std::decay_t<F> const&(Value<T> const&)>,
bool
>{}, int
> = 0
>
void Add(Value<T> &value, F&& f)
{
// do pass f Value<T>
}
template <class T, class F,
std::enable_if_t<
!std::is_convertible<
std::result_of_t<std::decay_t<F> const&(Value<T> const&)>,
bool
>{}
&& std::is_convertible<
std::result_of_t<std::decay_t<F> const&()>,
bool
>{}, int
> = 0
>
void Add(Value<T> &value, F&& f)
{
// do not pass f Value<T>
}

where we throw some nasty SFINAE detection on which of the two overloads you want to use, and explicitly prefer one.

This is not worth it.

When should I use std::bind?

Here's something you can't do with a lambda:

std::unique_ptr<SomeType> ptr = ...;
return std::bind(&SomeType::Function, std::move(ptr), _1, _2);

Lambdas can't capture move-only types; they can only capture values by copy or by lvalue reference. Though admittedly this is a temporary issue that's being actively resolved for C++14 ;)

"Simpler and clearer" is a matter of opinion. For simple binding cases, bind can take a lot less typing. bind also is focused solely on function binding, so if you see std::bind, you know what you're looking at. Whereas if you use a lambda, you have to look at the lambda implementation to be certain of what it does.

Lastly, C++ does not deprecate things just because some other feature can do what it does. auto_ptr was deprecated because it is inherently dangerous to use, and there is a non-dangerous alternative.



Related Topics



Leave a reply



Submit