By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
445,655 Members | 1,005 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 445,655 IT Pros & Developers. It's quick & easy.

Problems with std::less

P: n/a
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<intt1(1);
test<intt2(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?

Jun 10 '07 #1
Share this Question
Share on Google+
3 Replies


P: n/a
On 2007-06-10 14:28, ek wrote:
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<intt1(1);
test<intt2(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<Key>,
class Allocator=allocator<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<intt1(1);
test<intt2(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.

--
Erik Wikström
Jun 10 '07 #2

P: n/a
Erik Wikström wrote:
On 2007-06-10 14:28, ek wrote:
>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<intt1(1);
test<intt2(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<Key>,
class Allocator=allocator<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<intt1(1);
test<intt2(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?
Jun 10 '07 #3

P: n/a
On 2007-06-10 20:09, desktop wrote:
Erik Wikström wrote:
>On 2007-06-10 14:28, ek wrote:
>>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<intt1(1);
test<intt2(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<Key>,
class Allocator=allocator<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<intt1(1);
test<intt2(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?
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.

--
Erik Wikström
Jun 10 '07 #4

This discussion thread is closed

Replies have been disabled for this discussion.