...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
#include <boost/phoenix/scope/lambda.hpp>
A lot of times, you'd want to write a lazy function that accepts one or
more functions (higher order functions). STL algorithms come to mind, for
example. Consider a lazy version of stl::for_each
:
struct for_each_impl { template <typename C, typename F> struct result { typedef void type; }; template <typename C, typename F> void operator()(C& c, F f) const { std::for_each(c.begin(), c.end(), f); } }; function<for_each_impl> const for_each = for_each_impl();
Notice that the function accepts another function, f
as an argument. The scope of this function, f
,
is limited within the operator()
. When f
is called inside std::for_each
, it exists in a new scope, along
with new arguments and, possibly, local variables. This new scope is not
at all related to the outer scopes beyond the operator()
.
Simple syntax:
lambda [ lambda-body ]
Like let
, local variables
may be declared, allowing 1..N local variable declarations (where N ==
BOOST_PHOENIX_LOCAL_LIMIT
):
lambda(local-declarations) [ lambda-body ]
The same restrictions apply with regard to scope and visibility. The RHS (right hand side lambda-expression) of each local-declaration cannot refer to any LHS local-id. The local-ids are not in scope yet; they will be in scope only in the lambda-body:
lambda( _a = 1 , _b = _a // Error: _a is not in scope yet )
See let
Visibility for more information.
Example: Using our lazy for_each
let's print all the elements in a container:
for_each(arg1, lambda[cout << arg1])
As far as the arguments are concerned (arg1..argN), the scope in which
the lambda-body exists is totally new. The left arg1
refers to the argument passed to for_each
(a container). The right arg1
refers to the argument passed by std::for_each
when we finally get to call operator()
in our for_each_impl
above (a container element).
Yet, we may wish to get information from outer scopes. While we do not
have access to arguments in outer scopes, what we still have is access
to local variables from outer scopes. We may only be able to pass argument
related information from outer lambda
scopes through the local variables.
Note | |
---|---|
This is a crucial difference between |
Another example: Using our lazy for_each
,
and a lazy push_back
:
struct push_back_impl { template <typename C, typename T> struct result { typedef void type; }; template <typename C, typename T> void operator()(C& c, T& x) const { c.push_back(x); } }; function<push_back_impl> const push_back = push_back_impl();
write a lambda expression that accepts:
vector<vector<int> >
)
int
)
and pushes-back the element to each of the vector<int>
.
Solution:
for_each(arg1, lambda(_a = arg2) [ push_back(arg1, _a) ] )
Since we do not have access to the arguments of the outer scopes beyond
the lambda-body, we introduce a local variable _a
that captures the second outer argument: arg2
.
Hence: _a = arg2. This local variable is visible inside the lambda scope.
(See lambda.cpp)