Tải bản đầy đủ (.pdf) (6 trang)

O''''Reilly Network For Information About''''s Book part 83 ppt

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (22.44 KB, 6 trang )

function or a member function. There can be zero or more arguments to the
function, some of these can be set directly, some supplied when the function is
invoked. With the current version of Boost.Lambda, up to nine arguments are
supported (three of which can be applied later through the use of placeholders). To
use the binders, you need to include the header "boost/lambda/bind.hpp".
When binding to a function, the first argument is the address of the function, and
the subsequent arguments are the arguments. For a non-static class member
function, there is always an implicit this argument; in a bind expression, the
this argument must be explicitly added. For convenience, the syntax is the same
regardless of whether the object is passed by reference or by pointer. So, when
binding to a member function, the second argument (that is, the first after the
function pointer) is the actual object to which the function should be invoked. It's
even possible to bind to data members, which is also demonstrated in the following
example:
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
int main() {
using namespace boost::lambda;
typedef std::map<int,std::string> type;
type keys_and_values;
keys_and_values[3]="Less than pi";
keys_and_values[42]="You tell me";
keys_and_values[0]="Nothing, if you ask me";
std::cout << "What's wrong with the following expression?\n";
std::for_each(
keys_and_values.begin(),
keys_and_values.end(),


std::cout << "key=" <<
bind(&type::value_type::first,_1) << ", value="
<< bind(&type::value_type::second,_1) << '\n');
std::cout << "\n and why does this work as expected?\n";
std::for_each(
keys_and_values.begin(),
keys_and_values.end(),
std::cout << constant("key=") <<
bind(&type::value_type::first,_1) << ", value="
<< bind(&type::value_type::second,_1) << '\n');
std::cout << '\n';
// Print the size and max_size of the container
(std::cout << "keys_and_values.size()=" <<
bind(&type::size,_1) << "\nkeys_and_values.max_size()="
<< bind(&type::max_size,_1))(keys_and_values);
}
This example starts out with the creation of a std::map with keys of type int
and values of type std::string. Remember that the value_type of
std::map is a std::pair with the key type and the value type as members.
Thus, for our map, the value_type is std::pair<int,std::string>,
so in the for_each algorithm, the function object that we pass will receive such
a type. Given this pair, it would be nice to be able to extract the two members
(the key and the value), and that's exactly what our first bind expression does.
bind(&type::value_type::first,_1)
This expression yields a function object that, when invoked, retrieves the data
member first, of the nested type value_type, of its argument, the pair we
discussed earlier. In our example, first is the key type of the map, and is thus a
const int. This is exactly the same syntax as for member functions. But you'll
note that our lambda expression does a bit more; the first part of the expression is
std::cout << "key=" <<

This compiles, and it works, but it's probably not what's intended. This expression
is not a lambda expression; it's just an expression, period. When invoked, it prints
key=, but it is only invoked once when the expression is evaluated, not once for
each element visited by std::for_each. In the example, the intention is for
key= to be the prefix for each key/value pair of our keys_and_values. In
earlier examples, we wrote code similar to this, but it didn't exhibit this problem.
The reason is that we used a placeholder as the first argument to the
operator<<, which made it a valid lambda expression. Here, we must somehow
tell Boost.Lambda that it's supposed to create a function object including the
"key=". This is done with the function constant, which creates a nullary
function object, one that takes no arguments; it merely stores its argument, and
then returns it when invoked.
std::cout << constant("key=") <<
This little change makes all the difference, as shown by the output when running
this program.
What's wrong with the following expression?
key=0, value=Nothing, if you ask me
3, value=Less than pi
42, value=You tell me
and why does this work as expected?
key=0, value=Nothing, if you ask me
key=3, value=Less than pi
key=42, value=You tell me
keys_and_values.size()=3
keys_and_values.max_size()=4294967295
The final part of the example is a binder that binds to a member function rather
than a data member; the syntax is identical, and you'll note that in both cases,
there's no need to explicitly state the return type of the function. This magic is
achieved by automatically deducing the return type of the function or member
function, and the type if the binder refers to a data member. However, there is a

case where the return type cannot be deduced, and that's when a function object is
to be bound; for free functions and member functions, it's a straightforward task to
deduce the return type,
[2]
but for function objects it's impossible. There are two
ways around this limitation of the language, and the first is brought forth by the
Lambda library itself: overriding the return type deduction by explicitly stating it
as a template parameter to the call to bind, as demonstrated by the following
program.
[2]
Your mileage may wary. Let's just say that it's technically doable.
class double_it {
public:
int operator()(int i) const {
return i*2;
}
};
int main() {
using namespace boost::lambda;
double_it d;
int i=12;
// If you uncomment the following expression,
// the compiler will complain;
// it's just not possible to deduce the return type
// of the function call operator of double_it.
// (std::cout << _1 << "*2=" << (bind(d,_1)))(i);
(std::cout << _1 << "*2=" << (bind<int>(d,_1)))(i);
(std::cout << _1 << "*2=" << (ret<int>(bind(d,_1))))(i);
}
There are two versions of the mechanism that disables the return type deduction

systemthe shorthand version is simply passing the return type as a parameter to
bind, the second is by using ret, which must enclose any lambda/bind
expression where the automatic deduction would otherwise fail. This can quickly
become tedious in nested lambda expressions, but there is an even better way,
which allows the deduction to succeed. We'll cover that later in this chapter.
Also note that a bind expression can consist of another bind expression, which
makes binders a great tool for functional composition. There's plenty of power in
nested binds, but tread carefully, because with the power comes additional
complexity when reading, writing, and understanding the code.
I Don't Like _1, _2, and _3Can I Rename Them?
Some people aren't comfortable with the predefined placeholder names, so the
library offers a convenient way to change them
[3]
to anything the user wants. This
is accomplished by declaring variables of the type
boost::lambda::placeholderX_type, where X is 1, 2, or 3. For
example, assuming one prefers the names Arg1, Arg2, and Arg3 as names for
the placeholders:
[3]
Technically, to add new ones.
#include <iostream>
#include <vector>
#include <string>
#include "boost/lambda/lambda.hpp"
boost::lambda::placeholder1_type Arg1;
boost::lambda::placeholder2_type Arg2;
boost::lambda::placeholder3_type Arg3;
template <typename T,typename Operation>
void for_all(T& t,Operation Op) {
std::for_each(t.begin(),t.end(),Op);

}
int main() {
std::vector<std::string> vec;
vec.push_back("What are");
vec.push_back("the names");
vec.push_back("of the");
vec.push_back("placeholders?");
for_all(vec,std::cout << Arg1 << " ");
std::cout << "\nArg1, Arg2, and Arg3!";
}
The placeholder variables you declare this way work just like _1, _2, and _3. As
an aside, note the function for_all that is introduced hereit offers a convenient
way of avoiding some redundant typing when frequent operations are to be applied
to all elements of a containerthat is, when one would typically use for_each.
The function accepts two arguments: a reference to a container, and a function or
function object. For each element of this container, the element is applied to the
function or function objects. I tend to find it quite useful from time to timeperhaps
you will too. Running the program produces the following output:
What are the names of the placeholders?
Arg1, Arg2, and Arg3!
Creating your own placeholder names can be a liability for others reading your
code; most programmers who know Boost.Lambda (or Boost.Bind) will be
familiar with the placeholder names _1, _2, and _3. If you decide to call them q,
w, and e, you'll most likely need to explain what they mean to your coworkers.
(And you'll probably have to repeat the explanation often!)
I Want to Give My Constants and Variables Names!
Sometimes, the readability of the code can be improved by giving names to
constants and variables. As you'll recall, we must sometimes create a lambda
expression out of an expression that would otherwise be evaluated immediately.
This is done using either constant or var; they operate on constant and mutable

variables, respectively. We've already used constant, and var basically works the
same way. In complex or long lambda expressions, giving a name to one or more

×