views:

83

answers:

4

So I'm working on a project and I have to use the set library on class objects. Those objects have many attributes, ID being one of them.

What I wanted to do was search for an object inside a "set" by its ID. The problem is set only has find and I don't know how to search for an ID this way since I'd have to use find(class object) and not find(int). I tried messing with class operators to read it as an object but couldn't find a way.

Also, I thought about algorithm::find_if, but that would just check every element from beggining to end instead of using the set "tree" search functions, right?

Thanks in advance.

+2  A: 

You'll have to use the second template argument to specify a comparison functor. See the ltstr example on this page

Pace
The thing is I already did that since it was needed to insert the classes in the set correctly. That's also working as it inserts them according to their ID, but for find it still won't let me use find(int)...
True, you'll have to make a dummy instance with the ID you're looking for.
Pace
+2  A: 

From your description, you might want to consider using a std::map or std::unordered_map, with your "ID" as the key and your class object as the value.

Terry Mahaffey
I would agree with this, but his question states he has to use std::set.
RC
A: 

You need to create a constructor for your class that takes int as its only argument. Doing so allows implicit conversion from int to your class, making it possible to call std::set::find(int), as requested.

For example:

#include <iostream>
#include <set>

class Foo {
  public:
    /* Normal constructor */
    Foo(const char * s, int i) : str(s),id(i) {}
    /* Special constructor for implicit conversion */
    Foo(int i) : str(0),id(i) {}
    /* Make Foo usable with std::set */
    bool operator<(const Foo& f) const { return f.id<id; }
    /* Make Foo printable */
    friend std::ostream& operator<<(std::ostream& o, const Foo& f);
  private:
    const char * str;
    int id;
};
std::ostream& operator<<(std::ostream& o, const Foo& f) { 
  return o << "(" << f.str << " " << f.id << ")";
}

typedef std::set<Foo> FooSet;
int main(void) {
  FooSet s;
  s.insert(Foo("test",1));
  s.insert(Foo("asdf",7));
  s.insert(Foo("spam",3));
  for (int i=0; i<10; ++i) {
    /* Note that searching is done via FooSet::find(int id) */
    FooSet::const_iterator f = s.find(i);

    std::cout << "Searching for id " << i << ": ";
    if (f==s.end())
      std::cout << "absent";
    else
      std::cout << "present " << *f;
    std::cout << std::endl;
  }
  return 0;
}

This yields:

Searching for id 0: absent
Searching for id 1: present (test 1)
Searching for id 2: absent
Searching for id 3: present (spam 3)
Searching for id 4: absent
Searching for id 5: absent
Searching for id 6: absent
Searching for id 7: present (asdf 7)
Searching for id 8: absent
Searching for id 9: absent
mrkj
That's exactely what I wanted to know. It's working now, thanks!
+2  A: 

If your class is already compatable with a set then you have defined the operator< or have provided a specific comparitor for comparing elements using a strict weak ordering.

struct X
{
    X(int pid): id(pid) {} 
    int id;
    bool operator<(X const& rhs) { return this->id < rhs.id;}
};

std::set<X>  data;
std::set<X>::const_iterator find = data.find(X(12));
if (find != data.end())
{
       // You found the item
}

This has the drawbacks in that you need to define X in a way that you can easily create temporary objects with a specific ID and the operator< (or the comparitor) is just a strict weak ordering of the ID.

An alternative is to use std::find_if() with a custom comparitor:

struct TestXID
{
    TestXID(int testId): tid(testId) {}
    bool operator()(X const& item) const {return tid == item.id;}
    int  tid;
};

std::set<X>::const_iterator find = std::find(data.begin(),data.end(),TestXID(5));
if (find != data.end())
{
       // You found the item
}
Martin York
As the original poster mentioned, the drawback to std::find_if() is that it is linear in complexity, as opposed to O(log n) for std::set::find().
mrkj