1

I want to use specified equal_to function int unordered_set The sample code likes this:

struct myEqual
{       //string with one different character is considered equal
    bool operator()(const string &s1, const string &s2)const
    {

        if(s1.length() != s2.length()) return false;
        int dis = 0;
        for(unsigned int i = 0; i<s1.size(); i++)
        {
            if(s1[i] != s2[i])
            {
                dis++;
                if(dis >= 2) return false;
            }               
        }
        return true;
    }
};

int main()
{
    unordered_set<string, std::tr1::hash<string>, myEqual> myDict;
    myDict.insert("a");
    myDict.insert("b");
    myDict.insert("c");

    unordered_set<string, std::tr1::hash<string>, myEqual>::iterator it = myDict.find("k");
    if(it == myDict.end())
    {
        cout<<"myequal not work"<<endl;
    }
    else
    {
        cout<<*it<<endl;
    }
    return 0;
}

according to myEqual function, there are three values "a", "b", "c" that are "equal" to "k", however the find only return one iterator. Is there anyway to find all equal value?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304

2 Answers2

4

There are two problems here, neither of which have anything to do with finding the element:

  1. Since "a", "b" and "c" all compare equal to one another, you can only keep one of them in the unordered_set.
  2. You have elements that compare equal yet are likely to have different hash codes. This violates the contract that you have to fulfil in order for the unordered set to work correctly.

A more general issue to bear in mind is that your equivalence relationship is not transitive: "aa" equals "ab", and "ab" equals "bb". Yet "aa" does not equal "bb".

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • 3
    Re: different hash codes: good point. This container is probably a disaster. – Pete Becker Feb 13 '13 at 16:38
  • @PeteBecker even using the `std::unordered_multiset` member `equal_range` won't work with a non-transitive comparison http://liveworkspace.org/code/3FAlUx$37 – TemplateRex Feb 13 '13 at 17:01
0

The best approach would be to fix the following 4 things

  1. Use an std:unordered_multiset as container

  2. Use a mutually consistent hash function and key comparison function. In particular, the hash function needs to map equal elements to equal keys.

  3. Use a comparison function that is an equivalence relation. In particular, this means that if A==B and B==C, then A==C, where == corresponds to your myEqual.

  4. Use the member function equal_range that returns a pair of iterators. If the first iterator does not equal the end of your container, you can loop.

Code:

auto res = myDict.equal_range("k");
if(res.first == myDict.end())
{
    cout<<"myequal not work"<<endl;
}
else
{
    for (auto it = res.first; it != res.second; ++it) 
        cout<<*it<<endl;
}

Both your hash function and your comparison operator currently violate the Container Requirements of the C++ Standard Library. Your hash code does not map equal (in the sense of your myEqual) strings to equal hash keys. Your comparison operator is not transitive (see the example of @NPE). This means that you cannot use the std::unordered_set or std::unordered_multiset. If you do so, your code yields undefined behavior.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304