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

6 222 0
O''''Reilly Network For Information About''''s Book part 56 ppt

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

Thông tin tài liệu

boost::addable<simple_string, boost::addable<simple_string,const char*> > { The Difference Between Equality and Equivalence When defining relational operators for classes, it's important to make the distinction between equality and equivalence. An equivalence relation is required in order to use the associative containers, and it defines a strict weak ordering through the concept LessThanComparable. [11] This relation makes the least assumptions, and poses as few requirements as possible, for types that are to be used with the Standard Library containers. However, the difference between equality and equivalence can sometimes be confusing, and it is important to understand the difference. When a class supports the concept LessThanComparable, it typically also supports the notion of equivalence. If two elements are compared, and neither is less than the other, we can consider them to be equivalent. However, equivalence doesn't necessarily mean equal. For example, it may be reasonable to omit certain characteristics from a less than relation, but consider them for equality. [12] To illustrate this, let's look at a class, animal, which supports both an equivalence relation and an equality relation. [11] Capitalized concepts like LessThanComparable come straight from the C++ Standard. All of the concepts in Boost.Operators use lowercase names. [12] Which implies a strict weak ordering, but not a total ordering. class animal : boost::less_than_comparable<animal, boost::equality_comparable<animal> > { std::string name_; int age_; public: animal(const std::string& name,int age) :name_(name),age_(age) {} void print() const { std::cout << name_ << " with the age " << age_ << '\n'; } friend bool operator<(const animal& lhs, const animal& rhs) { return lhs.name_<rhs.name_; } friend bool operator==(const animal& lhs, const animal& rhs) { return lhs.name_==rhs.name_ && lhs.age_==rhs.age_; } }; Notice the difference between the implementation of operator< and that of operator==. Only the animal's name is part of the less than relation, whereas comparison of both the name and the age comprise the equality test. There is nothing wrong with this approach, but it can have interesting ramifications. Let's now put this class into action by storing some elements of the class in a std::set. Just like other associative containers, set only relies on the concept LessThanComparable. In the sample code that follows, we create four animals that are all different, and then try to insert them into a set, all while pretending we don't know that there is a difference between equality and equivalence. #include <iostream> #include <string> #include <set> #include <algorithm> #include "boost/operators.hpp" #include "boost/bind.hpp" int main() { animal a1("Monkey", 3); animal a2("Bear", 8); animal a3("Turtle", 56); animal a4("Monkey", 5); std::set<animal> s; s.insert(a1); s.insert(a2); s.insert(a3); s.insert(a4); std::cout << "Number of animals: " << s.size() << '\n'; std::for_each(s.begin(),s.end(),boost::bind(&animal::print,_1)); std::cout << '\n'; std::set<animal>::iterator it(s.find(animal("Monkey",200))); if (it!=s.end()) { std::cout << "Amazingly, there's a 200 year old monkey " "in this set!\n"; it->print(); } it=std::find(s.begin(),s.end(),animal("Monkey",200)); if (it==s.end()) { std::cout << "Of course there's no 200 year old monkey " "in this set!\n"; } } Running the program produces the following, utterly nonsensical, output. Number of animals: 3 Bear with the age 8 Monkey with the age 3 Turtle with the age 56 Amazingly, there's a 200 year old monkey in this set! Monkey with the age 3 Of course there's no 200 year old monkey in this set! The problem is not the age of the monkeyit very seldom isbut the failure to distinguish between two related concepts. First, when the four animals (a1, a2, a3, a4) are inserted into the set, the second monkey, a4, is actually not inserted at all, because a1 and a4 are equivalent. The reason is that std::set uses the expression !(a1<a4) && !(a4<a1) to decide whether there is already a matching element. Because the result of that expression is true (our operator< doesn't include the age), the insertion fails. [13] Then, when we ask the set to search for a 200 year old monkey using find, it supposedly locates such a beast. Again, this is because of the equivalence relation for animal, which relies on animal's operator< and thus, doesn't care about age. We use find again to locate the monkey in the set (a1), but then, to decide whether it matches, we call on operator== and find that the monkeys don't match. It's not hard to understand the difference between equality and equivalence when looking at these monkeys, but it is imperative to know which one is applicable for a given context. [13] A set, by definition, does not contain duplicates. Arithmetic Types The Operators library is especially useful when defining arithmetic types. There are many operators that must be defined for an arithmetic type, and doing it manually is a daunting, tedious task, with plenty of opportunity for errors or omissions. The concepts that are defined by the Operators library make it easy to define only the bare minimum of operators for a class, and have the rest supplied automagically. Consider a class that is to support addition and subtraction. Assume that this class uses a built-in type for its implementation. Now add the appropriate operators and be sure that they work with not only instances of that class, but also with the built-in types that are convertible to the implementation type. You'll need to provide 12 different addition and subtraction operators. The easier (and safer!) approach, of course, is to use the two-argument form of the addable and subtractable classes. Now suppose you need to add the set of relational operators, too. You could probably add the 10 operators needed yourself, but by now you know that the easiest thing is to use less_than_comparable and equality_comparable. Having done so, you'd have 22 operators for the cost of 6. However, you might also note that these concepts are common for value type classes. Indeed, instead of using those four classes, you could just use additive and totally_ordered. We'll start by deriving from all four of the concept classes: addable, subtractable, less_than_comparable, and equality_comparable. The class, limited_type, just wraps a built-in type and forwards any operation to that type. It limits the number of available operations, providing just the relational operators and those for addition and subtraction. #include "boost/operators.hpp" template <typename T> class limited_type : boost::addable<limited_type<T>, boost::addable<limited_type<T>,T, boost::subtractable<limited_type<T>, boost::subtractable<limited_type<T>,T, boost::less_than_comparable<limited_type<T>, boost::less_than_comparable<limited_type<T>,T, boost::equality_comparable<limited_type<T>, boost::equality_comparable<limited_type<T>,T > > > > > > > > { T t_; public: limited_type():t_() {} limited_type(T t):t_(t) {} T get() { return t_; } // For less_than_comparable friend bool operator<( const limited_type<T>& lhs, const limited_type<T>& rhs) { return lhs.t_<rhs.t_; } // For equality_comparable friend bool operator==( const limited_type<T>& lhs, const limited_type<T>& rhs) { return lhs.t_==rhs.t_; } // For addable limited_type<T>& operator+=(const limited_type<T>& other) { t_+=other.t_; return *this; } // For subtractable limited_type<T>& operator-=(const limited_type<T>& other) { t_-=other.t_; return *this; } }; This is a good example of how easy the implementation becomes when using the Operators library. Implementing the few operators that must be implemented to get support for the full set of operators is typically not that hard, and the class becomes much more understandable and maintainable than would otherwise have been the case. (Even if implementing those operators is hard, you can concentrate on getting just those few right.) The only potential problem with the class is the derivation from eight different operator classes which, when using base class chaining, is not as readable as one would like. We can greatly simplify our class by using composite concepts instead. template <typename T> class limited_type : boost::additive<limited_type<T>, boost::additive<limited_type<T>,T, boost::totally_ordered<limited_type<T>, boost::totally_ordered<limited_type<T>,T > > > > { This is much nicer, and it does save some typing, too. Use Operators Only When Operators Should Be Used . equivalence doesn't necessarily mean equal. For example, it may be reasonable to omit certain characteristics from a less than relation, but consider them for equality. [12] To illustrate this,. Then, when we ask the set to search for a 200 year old monkey using find, it supposedly locates such a beast. Again, this is because of the equivalence relation for animal, which relies on animal's. There are many operators that must be defined for an arithmetic type, and doing it manually is a daunting, tedious task, with plenty of opportunity for errors or omissions. The concepts that

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

Từ khóa liên quan

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

  • Đang cập nhật ...

Tài liệu liên quan