tags:

views:

232

answers:

4

I have a STL container full of billions of the following objects

pair<SomeClass*, SomeClass*>

I need some function of the following form

/*returns items sorted biggest first */

bool sortPredicate (pair<SomeClass*, SomeClass*>two,  pair<SomeClass*, SomeClass*> one)
{
  return ???;
}

Is there some trick I can use to very quickly compare pairs of pointers?

Edit 1: A clarification

In the end I just want to sort the list of pointer-pairs such that all of the duplicates are next to each other. Assume that there is no clear method in SomeClass that can be used for this purpose---I only have pointer pairs, and I want to find all identical pairs (in parallel). I thought a sort would do the trick, but if you can think of a better parallel method, let me know.

Edit 2: A clarification

Fixed my code (the arguments to the sort predicate were wrong--they should be pairs).

+4  A: 

Assuming your class has comparison operators:

bool sortPredicate (SomeClass *two,  SomeClass *one)
{
  return *two > *one;
}

If you just want to compare the pointer addresses, use std::greater<T>:

sort(container.begin(), container.end(), std::greater<SomeClass *>());

EDIT: OK, I really have no idea what you are trying to do now, with your most recent edit. Why not just use the default sort, if all you want to do is find duplicates?

rlbond
This does not compare the pointers, but uses the operator< of SomeClass.
Danvil
Well it's a little fuzzy from the question. In the code it says `/*returns items sorted biggest first */`, so I assumed he wanted to compare the classes. However, I added a solution for actually comparing the pointers.
rlbond
@Danvil: I think the (unspoken) point is that it usually doesn't make sense to compare pointers when sorting.
Dave
Who knows, but now conradlee can decide what he needs.
Danvil
I've tried to clear this up in my last revision of the question. Is my question clear now?
conradlee
`pair` seems to use `op<` instead of `std::less` for the comparison. So the result would be that you have no total order (although in practice, of course, you mostly have). So just to go portable, you seem to need to write your own comparator that uses `std::less` deeply on the pair members.
Johannes Schaub - litb
@litb: Yes, you seem to be correct according to the Standard. I think that is particularly odd.
rlbond
A: 

If I understand correctly Your predicate should have the following signature

bool sortPredicate(pair<SomeClass*, SomeClass*>& lhs, pair<SomeClass*, SomeClass*>& rhs);

I know nothing about Your class and if there is any natural order for it, so it's hard to guess how You want to sort it. In The comment You write that the biggest items should be first. I assume there is < operator for the class. How about this?

bool sortPredicate(pair<SomeClass*, SomeClass*>& lhs, pair<SomeClass*, SomeClass*>& rhs)
{
    if(!(*(lhs.first) < *(rhs.first) || *(rhs.first) < *(lhs.first))) // If there is == operator use it.
    {
        return *(rhs.second) < *(lhs.second);
    }
    else
    {
        return *(rhs.first) < *(lhs.first);
    }

}

EDIT: Ok thx for clarifying. How about this?

bool sortPredicate(pair<SomeClass*, SomeClass*>& lhs, pair<SomeClass*, SomeClass*>& rhs)
{
    if(lhs.first == rhs.first)
    {
        return rhs.second < lhs.second;
    }
    else
    {
        return rhs.first < lhs.first;
    }
}
Maciej Hehl
A: 

You should define an operator<on your pair class. I assume that your pair holds item1 and item2. So:

template <class T>
class pair{
private:
  T item1;
  T item2
public:
  // [...] other stuff goes here
  // here the comparing
  bool operator<(pair p){
    return (item1 < p.item1 || (item1 == p.item1 && item2 < p.item2));
  }
};

This solution assumes that the items have defined the < and the == operators.

I suppose I didn't meet what you were exactly looking for, but I recommend to overload the <, >, and == operators in your pair class.

phimuemue
You know there's is `std::pair` in STL right? And STL's pair's `operator<` is same as what you've defined.
KennyTM
@KennyTM: actually, the `operator<` for `std::pair` is slightly different, it avoids using `==`.
Steve Jessop
+8  A: 

It is a quirk of C++ that arbitrary pointers of the same type are not (necessarily) comparable with <, but are comparable with std::less.

Unfortunately, the operator< for std::pair is defined in terms of operator< on the components, not std::less.

So, assuming that you want two pairs to fall in the same sort position if and only if they point to the same two objects, you need:

// "less than"
template<typename T>
bool lt(const T &lhs, const T &rhs) {
    return std::less<T>()(lhs, rhs);
}

typedef std::pair<SomeClass*, SomeClass*> mypair;

bool sortPredicate(const mypair &lhs, const mypair &rhs) {
    return lt(lhs.first, rhs.first) 
        || (!lt(rhs.first, lhs.first) && lt(lhs.second, rhs.second));
 }

On pretty much any system you can name, this should compile to the same code as return lhs < rhs;, but that is not formally correct. If the referands of the pointers are all subobjects of the same object (for instance if you have a huge array and all the pairs point to elements of that one array), then operator< is OK for the pointers and hence OK for std::pair<pointer,pointer>.

If you want to pairs to fall in the same sort position if and only if the objects they point to sort the same, then you'd add the extra dereference:

bool sortPredicate(const mypair &lhs, const mypair &rhs) {
    return lt(*lhs.first, *rhs.first) 
        || (!lt(*rhs.first, *lhs.first) && lt(*lhs.second, *rhs.second));
 }

and perhaps you'd also add checks for null pointers, if those are permitted. Of course if you know that SomeClass really is a class type, not a pointer type, then you don't need to use std::less in the version above, just define operator< for SomeClass and:

inline bool lessptr(const SomeClass *lhs, const SomeClass *rhs) {
    if (lhs == 0) return rhs != 0;
    if (rhs == 0) return false;
    return *lhs < *rhs;
}

bool sortPredicate(const mypair &lhs, const mypair &rhs) {
    return lessptr(lhs.first, rhs.first) 
        || (!lessptr(rhs.first, lhs.first) && lessptr(lhs.second, rhs.second));
 }

You may or may not be able to optimise that a bit, since there are some repeated null checks performed in both the first and second calls to lessptr. If you care that much, see what the compiler does with it.

Steve Jessop
Let me guess - this: "lhs.second < rhs.second" was meant to be changed to "less(lhs.second, rhs.second)" too :)
Maciej Hehl
Yes. Yes it was.
Steve Jessop
+1 for mentioning that std::less guarantees a total order for pointers. But std::less is a class template, not a function. Instead of `less(lhs,rhs)` you have to write `std::less<SomeClass*>()(lhs,hrs)`, have you not?
sellibitze
Curses, yes, good point. Will fix.
Steve Jessop