Lambdas are more readable, more expressive, and may be more efficient than using std::bind
.
More readable
Suppose we want a function to set up an audible alarm that will go off an hour after it’s set and that will stay on for 30 seconds, with the alarm sound remaining undecided.
|
|
In C++14, it is very easy to write a lambda version, which is also straight-forward to read:
|
|
If we decide to use std::bind
, it looks like this:
|
|
Here, we used two extra bind
inside the outer bind
instead of passing now() + 1h
directly as an argument of std::bind
, because we want to defer evaluation of the timestamp expression until when setAlarm
is called, rather than when std::bind
is called. In C++11, however, the template type argument for the standard operator tempaltes can not be omitted, so the C++11 std::bind
equivalent look like this shxt:
|
|
Moreover, if we have another overloaded function for setAlarm
, which takes a new parameter specifying the alarm volume:
|
|
The lambda above continues to work, because overload resolution chooses the three-argument version of setAlarm
.
However, the std::bind
version fails to compile now, because compilers have no way to determine which of the two setAlarm
functions they should pass to std::bind
- by the function name alone it is ambiguous.
To specify the exactly which one we want, we need to cast setAlarm
to the proper function pointer type:
|
|
More efficient
The cast above introduces another side effect:
std::bind
passes a function pointer tosetAlarm
, which means inside the function call operator forsetSoundB
(i.e., the function call operator()
for the bind object), the call tosetAlarm
takes place through a function pointer, which generally can’t be fully inlined- inside the function call operator for
setSoundL
(i.e., the function call operator of the lambda’s closure class), the call tosetAlarm
is a normal function invocation, which can be inlined by compilers
This means using lambdas might generate faster code than using std::bind
.
More expressive
|
|
Moreover, the placeholders (e.g., _1
, _2
, etc.) are opaque in that it doesn’t specify how parameters are passed and stored in the bind object, by value or by reference. We have to memorize that it’s stored by value. Lambda, however, specifies capture mode very clearly.
Edge cases
In C++14, there’s no reasonable use case for std::bind
. In C++11, two constrained situations my be useful:
-
Move capture, as item 32 explains.
-
Polymorphic function objects: this takes use of bind object’s perfect forwarding ability
1 2 3 4 5 6 7 8 9 10 11
class PolyWidget { public: template<typename T> void operator()(const T& param) const; ... }; PolyWidget pw; auto boundPW = std::bind(pw, _1); boundPW(1942); // pass int to PolyWidget::operator() boundPW(nullptr); // pass nullptr to PolyWidget::operator() boundPW("RoseGun"); // pass string literal to PolyWidget::operator()
In C++14, simply do this combining lambda with
auto
:1
auto boundPW = [pw](const auto& param) { pw(param); };