tags:

views:

213

answers:

4

I have a complex struct i want to put as a key of the std::map to make a list of all unique objects fast:

union somecomplexstruct {
     struct {
        more_structs val1, val2;
        even_more_structs val3, val4;
        lots_of_more_structs val5;
     };
     unsigned int DATA[3];
};

typedef map<somecomplexstruct, int, greater<somecomplexstruct> > somecomplexstructMap;

But it says error: error C2784: 'bool std::operator >(const std::vector<_Ty,_Alloc> &,const std::vector<_Ty,_Alloc> &)' : could not deduce template argument for 'const std::vector<_Ty,_Alloc> &' from 'const somecomplexstruct'

How do i make my struct work there?

Edit: Got it working, thanks to everyone! Heres the code:

inline bool operator>(const somecomplexstruct &v1, const somecomplexstruct &v2){
    if(v1.DATA[0] > v2.DATA[0]) return 1;
    if(v1.DATA[0] < v2.DATA[0]) return 0;
    if(v1.DATA[1] > v2.DATA[1]) return 1;
    if(v1.DATA[1] < v2.DATA[1]) return 0;
    return v1.DATA[2] > v2.DATA[2];
}
+8  A: 

std::greater<> invokes operator>() to do its work, so you need to overload that if you want to use std::greater<>.

It should look like this:

inline bool operator>(const somecomplexstruct& lhs, const somecomplexstruct& rhs)
{
  // implement your ordering here. 
}
sbi
Hmm, i cant quite get the logic in that function, when i need to only create unique objects list, should i change my map somehow, because this operator > doesnt make much sense. Or does it even matter what i put inside that function, because its only used for sorting, but not for testing if the object is equal to some other object in the list already? So if i want, i could as well just return 1 in that function always? OR does this affect performance or something? I am having hard time creating the test to see if the data is greater than other data.or should i even care if its "perfect"or not?
Newbie
I was thinking to make union from the struct, then compare `unsigned int` array values like: `inline bool operator>(const somecomplexstruct }` But it doesnt look like a right solution to this... Because not all the bits need to be greater, but if i use || in between `unsigned int` tests, then it also fails... so some help would be appreciated here
Newbie
rturrado
Could i as well just return 1 if i dont care about the order?
Newbie
@Newbie: `std::map` uses the ordering in order to efficiently determine whether it has a given key in the map. It's normally implemented as a balanced binary search tree. If you can't provide an order, then you could instead provide a hash function and use the non-standard `boost::unordered_map`, or `hash_map` if your C++ implementation offers it. If you can't provide either an order or a hash function, then you can't efficiently test for duplicates, and you might as well just use a `vector<pair<Key, Value> >` and search it linearly.
Steve Jessop
@Newbie: the most common way to produce an order is a so-called "lexicographic order". So if struct X contains a Y and a Z, then X's order function is something like `if (lhs.y < rhs.y) return true; if rhs.y < lhs.y) return false; return (lhs.z < rhs.z);`. You can extend this for things with multiple members, and you'll have to define an ordering similarly for any structs contained in the one you care about.
Steve Jessop
The reason why im using a struct in the map is because i am afraid of hash collisions :P And since the data size is only 3 times that, i dont see much performance problems different from a hash...
Newbie
@Steve Jessop: is there a typo in your code? where is x checks? and shouldnt it be `lhs.y < rhs.y` instead of `rhs.y < lhs.y` ?
Newbie
So, should this be ok? `if(v1.DATA[0] > v2.DATA[0]) return 1; if(v1.DATA[1] > v2.DATA[1]) return 1; return v1.DATA[2] > v2.DATA[2];` I made union from the struct and now im comparing unsigned ints from the struct raw data. (note: v1 = lhs, v2 = rhs)
Newbie
Newbie
lol, i tried with `return v1.DATA[0] > v2.DATA[0]` and it was 3 times faster than my previous fastest solution :S Am i doing it right?
Newbie
Newbie
Newbie
`if(v1.DATA[0] > v2.DATA[0]) return true; if(v1.DATA[1] > v2.DATA[1]) return true; return v1.DATA[2] > v2.DATA[2];` seems to be the correct one(?), i must check it with another method to be sure about the unique items count...
Newbie
I made it work with converting the data into string HEX code into string map. i cant get it work any other way, so im still seeking for a struct method for this...
Newbie
Most probably you've written a wrong comparator. Search or post a separate question on how to write a comparator for your key structure.
Basilevs
By the way, `{ return }` is NOT a good comparator. The `map` can and will make copies of its inserted arguments, and those copies won't compare the same way as the original if their addresses are used, causing undefined behavior.
aschepler
Java style in action :)
Basilevs
+1  A: 

Here is lexicographical comparator for a complex structure

struct D {
  struct A {
    bool operator <(const A &) const;
  } a;
  struct B {
    bool operator <(const B &) const;
  } b;
  struct C {
    bool operator <(const C &) const;
  } c;
  template <class T> ne(const T & a, const T & b) {
    if (a < b) return true;
    if (b < a) return true;
    return false;
  }
  bool operator < (const D & that) const {
    if (ne(a, that.a)) return a < that.a;
    if (ne(b, that.b)) return b < that.b;
    return c < that.c;
  }
};
Basilevs
Thanks. I'll fix consts, and leave members for the sake of shorter declarations.
Basilevs
I get this error: `error C2678: binary '!=' : no operator found which takes a left-hand operand of type 'const D::A' (or there is no acceptable conversion)` from that struct
Newbie
Sorry for that, I applied a quick fix, to prevent this error.
Basilevs
+3  A: 

With your operator> function, consider what happens if you compare {1, 0, 0} and {0, 1, 0}. If you compare a > b, it returns true from the first comparison. If you compare b > a it returns true from the second comparison. So its fails the reflexive property for comparisons, scrambling the map. In order for map to work properly, you must define your operator> such that a > b == !(b > a) for all possible non-equal pairs of values that might be compared.

edit

The easiest/best way to ensure that your operator is properly reflexive is to ensure the for every test that might return true, you also have a test with the same condition and the operands swapped that returns false. So if you have

if(v1.DATA[1] > v2.DATA[1]) return 1;

in your function, you need

if(v2.DATA[1] > v1.DATA[1]) return 0;

or the equivalent somewhere.

Chris Dodd
Yeah, how do i do that...
Newbie
- @Newbie think.
wilhelmtell
i see... i got it worknig now
Newbie
A: 

If your map contains only pointers to your struct, you don't have to do all that complicated operator overloading.

Therefore your typedef looks like this:

typedef map<somecomplexstruct*, int, greater<somecomplexstruct*> > somecomplexstructMap;

Structs typically have only public data members and no need for operator overloading.

This means though that you have to be careful about when and how you release the memory for the pointers. There's pros and cons to each approach, as with everything.

theninjagreg