1. Trang chủ
  2. » Công Nghệ Thông Tin

HandBooks Professional Java-C-Scrip-SQL part 56 pdf

6 126 0
Tài liệu đã được kiểm tra trùng lặp

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 21,53 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

boost::addable { The Difference Between Equality and Equivalence When defining relational operators for classes, it's important to make the distinction between equality and equivalence.

Trang 1

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_;

Trang 2

}

};

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()) {

Trang 3

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

Trang 4

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

Trang 5

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

Trang 6

Use Operators Only When Operators Should Be Used

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