Sometimes it is convenient to store lambda functors in variables. However, the types of even the simplest lambda functors are long and unwieldy, and it is in general unfeasible to declare variables with lambda functor types. The Boost Function library [function] defines wrappers for (almost) arbitrary function objects; and these wrappers have types that are easy to type out. For example:
int foo(float, char); boost::function<int, float, char> f = foo; boost::function<int, int, int> g = std::plus<int>();The return and parameter types of the wrapped function object must be written explicilty as template arguments to the wrapper template boost::function; even when lambda functors, which otherwise have generic parameters, are wrapped. Wrapping a function object with boost::function introduces a performance cost comparable to virtual function dispatch, though virtual functions are not actually used.
Due to a technical conflict between the two libraries, lambda functors cannot be directly wrapped with boost::function (this may be resolved in the future). However, applying the unlambda (see Section 5.9.1.1) function to a lambda functor gives a function object that is compatible with boost::function. For example:
boost::function<int, int, int> f = unlambda(_1 + _2); f(1, 2); // returns 3
int i = 1; boost::function<int&, int&> g = unlambda(_1 += 10); g(i); // i == 11;Note that storing lambda functors inside boost::function introduces a danger. Certain types of lambda functors may store references to the bound arguments, instead as taking copies of the arguments of the lambda expression. When temporary lambda functor objects are used in STL algorithm invocations this is always safe, as the lambda functor gets destructed immediately after the STL algortihm invocation is completed. However, a lambda functor wrapped inside boost::function may continue to exist longer, creating the possibility of dangling references. For example:
int* sum = new int(); *sum = 0; boost::function<int&, int> counter = unlambda(*sum += _1); counter(5); // ok, *sum = 5; delete sum; counter(3); // error, *sum does not exist anymore
The Boost Bind [bind] library has partially overlapping functionality with the BLL. Basically, the Boost Bind library (BB in the sequel) implements the bind expression part of BLL. There are, however, some semantical differerences.
The BLL and BB evolved separately, and have different implementations. This means that the bind expressions from the BB cannot be used within bind expressions, or within other type of lambda expressions, of the BLL. The same holds for using BLL bind expressions in the BB. The libraries can coexist, however, as the names of the BB library are in boost namespace, whereas the BLL names are in boost::lambda namespace.
The BLL requires a compiler that is reasonably conformant to the C++ standard, whereas the BB library is more portable, and works witha a larger set of compilers.
The following two sections describe what are the semantic differences between the bind expressions in BB and BLL.
template<class F>
int foo(const F& f) {
int x;
..
bind(f, _1)(x);
...
}
int bar(int, int); nested(bind(bar, 1, _1));The bind expression inside foo becomes:
bind(bind(bar, 1, _1), _1)(x)The BLL interpretes this as:
bar(1, x)(x)whereas the BB library as
bar(1, x)To get this functionality in BLL, the bind expression inside the foo function can be written as:
bind(unlambda(f), _1)(x);as explained in Section 5.9.1.1.
In both libraries, the highest placeholder index in a bind expression determines the arity of the resulting function object. However, in the BB library, this is kind of a minimal arity, as the function object can take arbitrarily many arguments; those not needed are discarded. Consider the two bind expressions and their invocations below:
bind(g, _3, _3, _3)(x, y, z); bind(g, _1, _1, _1)(x, y, z);This first line ends up making the call:
g(z, z, z)in both libraries. The second line is treated differently. In BLL a compile time error will result, whereas BB will silently ignore the superfluous arguments and invoke:
g(x, x, x)In this tradeoff between safety and flexibility, BLL takes the safer route. Note however, that it is easy to write a lambda functor that would make the above call to g(x, x, x) discarding all but the first argument:
(_3, bind(g, _1, _1, _1))(x, y, z);This lambda expression takes three arguments. The left-hand argument of the comma operator does nothing, and as comma returns the result of evaluating the right-hand argument we end up with the call g(x, x, x).