user228938
user228938

Reputation: 187

How do I use a class as a value to be used on set::find()? - C++

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.

Upvotes: 0

Views: 288

Answers (4)

mrkj
mrkj

Reputation: 3101

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

Upvotes: 0

Loki Astari
Loki Astari

Reputation: 264411

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
}

Upvotes: 2

Terry Mahaffey
Terry Mahaffey

Reputation: 11981

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.

Upvotes: 2

Pace
Pace

Reputation: 43817

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

Upvotes: 2

Related Questions