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

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

Đang tải... (xem toàn văn)

Thông tin tài liệu

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 . 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 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 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="

Ngày đăng: 07/07/2014, 08:20

Tài liệu cùng người dùng

Tài liệu liên quan