Problems with std::less

E

ek

I have the following class:

template<typename I>
class test {
public:
test(I i) : pp(i) {}

I getpp() const {
return pp;
}

void setpp(I i) {
pp = i;
}

bool operator<(const test& t) const {
return (this < &t);
}


private:
I pp;
};

In main I do:

int main(){

test<int> t1(1);
test<int> t2(2);

std::less<test<int> > C;
std::cout << "C(t1,t2) = " << C(t1,t2) << std::endl;
std::cout << "C(t1,t1) = " << C(t1,t1) << std::endl;

return 0
}

But it prints 0 in both cases. How do I compare objects? When I supply
these objects to a std::set the '<' operator in my class should work
so at the moment the tree would not be balanced correctly.


Another thing. I don't see the point in using 'C' I can just use the
operator '<' directly:

std::cout << "t1 < t2 = " << (t1 < t2) << std::endl;
std::cout << "t1 < t1 = " << (t1 < t1) << std::endl;

which gives the same result, so why go through the extra layer with
using std::less which just calls the '<' anyway instead of just
calling '<' directly?
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

I have the following class:

template<typename I>
class test {
public:
test(I i) : pp(i) {}

I getpp() const {
return pp;
}

void setpp(I i) {
pp = i;
}

bool operator<(const test& t) const {
return (this < &t);
}


private:
I pp;
};

In main I do:

int main(){

test<int> t1(1);
test<int> t2(2);

std::less<test<int> > C;
std::cout << "C(t1,t2) = " << C(t1,t2) << std::endl;
std::cout << "C(t1,t1) = " << C(t1,t1) << std::endl;

return 0
}

But it prints 0 in both cases. How do I compare objects? When I supply
these objects to a std::set the '<' operator in my class should work
so at the moment the tree would not be balanced correctly.

To compare two objects you have to decide what the properties that
defines the object are, and then how you based on this order objects. In
the code above you have decide that the address of the object determines
which is the smallest one. This is generally a bad idea since the result
of comparing two objects might not be the same as when two copies of
them are compared.

In your case the natural thing to compare would be the value of pp, so
change your operator < to:

bool operator<(const test& t) const {
return (pp < t.pp);
}

and you'll get the expected results when you run the program.
Another thing. I don't see the point in using 'C' I can just use the
operator '<' directly:

std::cout << "t1 < t2 = " << (t1 < t2) << std::endl;
std::cout << "t1 < t1 = " << (t1 < t1) << std::endl;

which gives the same result, so why go through the extra layer with
using std::less which just calls the '<' anyway instead of just
calling '<' directly?

The idea of using std::less in a container is that the user can specify
how they want their objects compared, if we take std::set as an example
it has the following signature (from VC++2005):

template <class Key,
class Traits=less said:
class set;

So, Key is the type of object you want to store in the set, Traits tells
you how to compare two objects, and Allocator how they are allocated. As
you can see the default comparator is std::less, which means that the
objects < operator will be used, but you can change this to std::greater
to use the objects' > instead, or you can create a specialized one that
compares the results of calling foo(5) on the objects.

If you add an operator > like this to test:

bool operator>(const test& t) const {
return (this->pp > t.pp);
}

And take a look at this code which implements a class that keeps a copy
of an object:

template<class T, class Comp>
class KeepOne
{
T theOne;
Comp C;
public:
KeepOne(T init) : theOne(init) {}
void insert(T t)
{
if (C(t, theOne))
theOne = t;
}
T& get() { return theOne; }
};

int main()
{
test<int> t1(1);
test<int> t2(2);

KeepOne<test<int>, std::less<test<int> > > k(t2);
k.insert(t1);
std::cout << k.get().getpp();
}


As you can see, when you insert() into the KeepOne object it will only
keep one object and the one that it keeps is determined by the
comparator used. Replace std::less<test<int> > with
std::greater<test<int > > and you'll see that it will keep t2 instead of t1.
 
D

desktop

Erik said:
To compare two objects you have to decide what the properties that
defines the object are, and then how you based on this order objects. In
the code above you have decide that the address of the object determines
which is the smallest one. This is generally a bad idea since the result
of comparing two objects might not be the same as when two copies of
them are compared.

In your case the natural thing to compare would be the value of pp, so
change your operator < to:

bool operator<(const test& t) const {
return (pp < t.pp);
}

and you'll get the expected results when you run the program.


The idea of using std::less in a container is that the user can specify
how they want their objects compared, if we take std::set as an example
it has the following signature (from VC++2005):

template <class Key,

class set;

So, Key is the type of object you want to store in the set, Traits tells
you how to compare two objects, and Allocator how they are allocated. As
you can see the default comparator is std::less, which means that the
objects < operator will be used, but you can change this to std::greater
to use the objects' > instead, or you can create a specialized one that
compares the results of calling foo(5) on the objects.

If you add an operator > like this to test:

bool operator>(const test& t) const {
return (this->pp > t.pp);
}

And take a look at this code which implements a class that keeps a copy
of an object:

template<class T, class Comp>
class KeepOne
{
T theOne;
Comp C;
public:
KeepOne(T init) : theOne(init) {}
void insert(T t)
{
if (C(t, theOne))
theOne = t;
}
T& get() { return theOne; }
};

int main()
{
test<int> t1(1);
test<int> t2(2);

KeepOne<test<int>, std::less<test<int> > > k(t2);
k.insert(t1);
std::cout << k.get().getpp();
}


As you can see, when you insert() into the KeepOne object it will only
keep one object and the one that it keeps is determined by the
comparator used. Replace std::less<test<int> > with
std::greater<test<int > > and you'll see that it will keep t2 instead of
t1.

Ok but the more general idea is that the point of using a Comparator is
that you don't need to change the code in the container that uses the
comparator. As long as you implement the operators in the objects being
stored you can just change the Comparator instead of writing a copy of
the container where it uses < instead og >, right?
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Ok but the more general idea is that the point of using a Comparator is
that you don't need to change the code in the container that uses the
comparator. As long as you implement the operators in the objects being
stored you can just change the Comparator instead of writing a copy of
the container where it uses < instead og >, right?

Not quite, I'd say that the idea is more that if you have no way to
change the containers (while it's theoretically possible to change the
standard containers most people wouldn't) and you can't change the
objects to be stored either you can instead create your own comparator,
that way you can use the standard containers with any objects without
even if they were not designed to be stored in such.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top