Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make find() work with a set of structs?

I am using a set to hold structs which contain several strings. I want to be able to use the find() functionality of sets. However, since the set is holding structs, it doesn't work. I want find() to look only at one of the strings in the struct. How can this be done?

Here's the code that I tried to use. It works fine except for the part where find() is used:

#include <iostream>
#include <string>
#include <set>
using namespace std;

struct test
{
    string key;
    string data;
};

bool operator<(const test & l, const test & r)
{
    return l.key < r.key;
}

bool operator==(const test & l, const test & r)
{
    return l.key == r.key;
}

set<test> s;

int main()
{
    test newmember;
    newmember.key = "key";
    newmember.data = "data";
    s.insert(newmember);
    s.find("key");
}

Here are the error messages that I get when I try to compile it:

test.cpp:30:7: error: no matching member function for call to 'find'
    s.find("key");
    ~~^~~~
In file included from test.cpp:3:
In file included from /usr/include/c++/4.2.1/set:65:
/usr/include/c++/4.2.1/bits/stl_set.h:429:7: note: candidate function not viable: no known conversion from 'const char [4]' to 'const key_type' (aka 'const test') for 1st argument
      find(const key_type& __x)
      ^
/usr/include/c++/4.2.1/bits/stl_set.h:433:7: note: candidate function not viable: no known conversion from 'const char [4]' to 'const key_type' (aka 'const test') for 1st argument
      find(const key_type& __x) const
      ^
1 error generated.
like image 951
node ninja Avatar asked May 03 '11 05:05

node ninja


2 Answers

I suggest you operator< and operator== to your struct instead of overloading the global operator, I find it much cleaner; example:

struct test
{
  string key;
  string data;

  bool operator<(const test& rhs) const
  {
    return key < rhs.key;
  }

  bool operator==(const test& rhs) const
  {
    return key == rhs.key;
  }
};

Now on to your real problem - your are passing a string to the find() function, but it only accepts structs of type test. In order to do so, add a constructor for automatic conversion, so the final struct would look like this:

struct test
{      
  string key;
  string data;

  test(const std::string& strKey = "", const std::string& strData = "")
  : key(strKey),
    data(strData) {}

  bool operator<(const test& rhs) const
  {
    return key < rhs.key;
  }

  bool operator==(const test& rhs) const
  {
    return key == rhs.key;
  }
};

Then passing a string to find() would automatically call the constructor and create a temporary test struct containing only the relevant key. Note that in this special case, the constructor must not be declared explicit.

like image 180
Mephane Avatar answered Sep 27 '22 20:09

Mephane


To be able to put your structs into set you have to specify operator< for your struct. You can make the operator< return result from comparing corresponding string members.

To be able to use find you can specify operator== for your struct to return true if corresponding string members are equal.

Sample:

    // code from your question used here

    int main()

{
    test newmember;
    newmember.key = "key";
    newmember.data = "data";

    test findMember;
    findMember.key = "key";
    // as operator== and operator< doesn't care about data field we can left it be
    // initialized by default constructor

    s.insert(newmember);
    s.find(findMember);
}

If you want to call find() with string parameter you can provide an implicit constructor from string for your test struct for example like this:

struct test {
//...
  test(const string &in_key) : key(in_key) {}
//...
};

But usage of implicit constructors isn't a good technique, because it can lead to some unpredictable conversions somewhere further in your code.

like image 33
beduin Avatar answered Sep 27 '22 18:09

beduin