O''''Reilly Network For Information About''''s Book part 85 pot

6 57 0
O''''Reilly Network For Information About''''s Book part 85 pot

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

Thông tin tài liệu

// Transform using std::bind1st and std::plus std::transform(vec.begin(),vec.end(),vec.begin(), std::bind1st(std::plus<int>(),4)); // Transform using a lambda expression std::transform(vec.begin(),vec.end(),vec.begin(),_1-=4); } The difference is astounding! When adding 4 using "traditional" means, it's hard for the untrained eye to see what's going on. Reading the code, we see that we are binding the first argument of a default-constructed instance of std::plus to 4. The lambda expression spells it outsubtract 4 from the element. If you think that the version using bind1st and plus isn't that bad, try it with longer expressions. Boost.Lambda supports all of the arithmetic operators in C++, so there's rarely a need to include <functional> just for the sake of arithmetic function objects. The following example demonstrates the use of some of those arithmetic operators. Each element in the vector vec is modified using the additive and multiplicative operators. #include <iostream> #include <vector> #include <algorithm> #include "boost/lambda/lambda.hpp" int main() { using namespace boost::lambda; std::vector<int> vec(3); vec[0]=1; vec[1]=2; vec[2]=3; std::for_each(vec.begin(),vec.end(),_1+=10); std::for_each(vec.begin(),vec.end(),_1-=10); std::for_each(vec.begin(),vec.end(),_1*=3); std::for_each(vec.begin(),vec.end(),_1/=2); std::for_each(vec.begin(),vec.end(),_1%=3); } Terse, readable, and maintainablethat's the kind of code you get with Boost.Lambda. Skip std::plus, std::minus, std::multiplies, std::divides, and std::modulus; your code is always better with Boost.Lambda. Writing Readable Predicates Many of the algorithms in the Standard Library come in a version that accepts a unary or binary predicate. These predicates are free functions of function objects, but of course, a lambda expression also fits the bill. For predicates that are used often, it makes perfect sense to define function objects, but frequently, they are used once or twice and then never looked at again. In such cases, a lambda expression is a superior choice, both because the code becomes easier to understand (all functionality resides at the same location), and because the code isn't cluttered with function objects that are rarely used. As a concrete example, consider finding an element with a specific value in a container. If operator== is defined for the type, it's easy to use the algorithm find directly, but what if another criteria for the search is to be used? Given the type search_for_me in the following, how would you use find to search for the first element where the member function a returns "apple"? #include <iostream> #include <algorithm> #include <vector> #include <string> class search_for_me { std::string a_; std::string b_; public: search_for_me() {} search_for_me(const std::string& a,const std::string& b) : a_(a),b_(b) {} std::string a() const { return a_; } std::string b() const { return b_; } }; int main() { std::vector<search_for_me> vec; vec.push_back(search_for_me("apple","banana")); vec.push_back(search_for_me("orange","mango")); std::vector<search_for_me>::iterator it= std::find_if(vec.begin(),vec.end(),???); if (it!=vec.end()) std::cout << it->a() << '\n'; } First of all, note that we need to use find_if, [5] but how should the predicate marked with ??? in the preceding code be defined? Here's one way: a function object that implements the logic for the predicate. [5] find uses operator==; find_if requires an additional predicate function (or function object). class a_finder { std::string val_; public: a_finder() {} a_finder(const std::string& val) : val_(val) {} bool operator()(const search_for_me& s) const { return s.a()==val_; } }; This function object can be used like so: std::vector<search_for_me>::iterator it= std::find_if(vec.begin(),vec.end(),a_finder("apple")); That's fine, but two minutes (or days) later, we'll want another function object, this time one that tests the member function b. And so on…this sort of thing quickly becomes tedious. As you've no doubt guessed, this is another excellent case for lambda expressions; we need the flexibility of creating the predicate directly where and when it's needed. The preceding find_if could have been written like this. std::vector<search_for_me>::iterator it= std::find_if(vec.begin(),vec.end(), bind(&search_for_me::a,_1)=="apple"); We bind to the member function a, and we test it for equality with "apple" and that's our unary predicate in full, defined right where it's used. But wait, as they say, there's more. When dealing with numeric types, we have the full range of arithmetic operators, comparisons, and logical operations to choose from. This means that even complex predicates are straightforward to define. Read the following code carefully, and see how well the predicates are expressed. #include <iostream> #include <algorithm> #include <vector> #include <string> #include "boost/lambda/lambda.hpp" int main() { using namespace boost::lambda; std::vector<int> vec1; vec1.push_back(2); vec1.push_back(3); vec1.push_back(5); vec1.push_back(7); vec1.push_back(11); std::vector<int> vec2; vec2.push_back(7); vec2.push_back(4); vec2.push_back(2); vec2.push_back(3); vec2.push_back(1); std::cout << *std::find_if(vec1.begin(),vec1.end(), (_1>=3 && _1<5) || _1<1) << '\n'; std::cout << *std::find_if(vec2.begin(),vec2.end(), _1>=4 && _1<10) << '\n'; std::cout << *std::find_if(vec1.begin(),vec1.end(), _1==4 || _1==5) << '\n'; std::cout << *std::find_if(vec2.begin(),vec2.end(), _1!=7 && _1<10) << '\n'; std::cout << *std::find_if(vec1.begin(),vec1.end(), !(_1%3)) << '\n'; std::cout << *std::find_if(vec2.begin(),vec2.end(), _1/2<3) << '\n'; } As you can see, creating such predicates is as easy as writing down the logic in the first place. This is one of my favorite uses for lambda expressions, because they can be understood by just about anyone. It's inevitable that we sometimes need to choose other mechanisms than lambda expressions simply because of the competence profiles of those who must understand the code; but here, there's nothing but added value. Make Your Function Objects Play Nicely with Boost.Lambda Not all expressions are suitable as lambda expressionscomplex expressions are better suited for regular function objects, and expressions that are reused as-is many times should also be made first-class citizens of your code base. They should be collected in a library of reusable function objects. But, you'll likely want to use these function objects in lambda expressions, too, and you'll want them to play nicely with Lambda; not all function objects do. The problem is that the return type of function objects cannot be deduced the way ordinary functions can; this is an inherent limitation of the language. However, there is a well-defined way for providing this important information to the Lambda library, which in turn makes bind expressions much cleaner. To give an example of the problem, consider the following function object: template <typename T> class add_prev { T prev_; public: T operator()(T t) { prev_+=t; return prev_; } }; Given such a function object, a lambda expression cannot deduce the return type, so the following example doesn't compile. #include <iostream> #include <algorithm> #include <vector> #include "boost/lambda/lambda.hpp" #include "boost/lambda/bind.hpp" int main() { using namespace boost::lambda; std::vector<int> vec; vec.push_back(5); vec.push_back(8); . std: :for_ each(vec.begin(),vec.end(),_1+=10); std: :for_ each(vec.begin(),vec.end(),_1-=10); std: :for_ each(vec.begin(),vec.end(),_1*=3); std: :for_ each(vec.begin(),vec.end(),_1/=2); std: :for_ each(vec.begin(),vec.end(),_1%=3);. Transform using std::bind1st and std::plus std::transform(vec.begin(),vec.end(),vec.begin(), std::bind1st(std::plus<int>(),4)); // Transform using a lambda expression std::transform(vec.begin(),vec.end(),vec.begin(),_1-=4);. std::vector<search _for_ me> vec; vec.push_back(search _for_ me("apple","banana")); vec.push_back(search _for_ me("orange","mango")); std::vector<search _for_ me>::iterator

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