Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to use unordered_set::contains() with a custom class?

I’m using std::unordered_set for the first time with a custom type, and I can’t figure out what I’m missing to make contains() compile for my set.

I basically have a class which looks something like this:

class MyClass
{
// Ctor/Dtor
public:
    MyClass(const std::string& name /* + other params in my real use case */) : m_name(name) {}
    ~MyClass() = default;

// Members
private:
    std::string m_name;

// Operators
public:
    // Equal operators
    bool operator==(const MyClass &other) const
    {
        return m_name == other.m_name;
    }

    bool operator==(const std::string& other) const
    {
        return m_name == other;
    }

    bool operator==(const char* other) const
    {
        return m_name == other;
    }

// Functors
public:
    // Hash functor
    struct Hash
    {
        std::size_t operator()(const MyClass &class_) const
        {
            return std::hash<std::string>()(class_.m_name);
        }

        std::size_t operator()(const std::string &name) const
        {
            return std::hash<std::string>()(name);
        }

        std::size_t operator()(const char* name) const 
        {
            return std::hash<std::string>()(name);
        }
    };

    // Equal functor
    struct Equal
    {
        bool operator()(const MyClass &lhs, const MyClass &rhs) const
        {
            return lhs.m_name == rhs.m_name;
        }

        bool operator()(const MyClass &lhs, const std::string &rhs) const
        {
            return lhs.m_name == rhs;
        }

        bool operator()(const std::string &lhs, const MyClass &rhs) const
        {
            return lhs == rhs.m_name;
        }

        bool operator()(const MyClass &lhs, const char* rhs) const
        {
            return lhs.m_name == rhs;
        }

        bool operator()(const char* lhs, const MyClass &rhs) const
        {
            return lhs == rhs.m_name;
        }
    };

};

Using this class, I want to create a std::unordered_set and check if the element who’s name is key1 exists. To do so, I’d like to use the following code:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

int main()
{
    // I tried both, but none of them seem to work
    std::unordered_set<MyClass, MyClass::Hash, MyClass::Equal> set;
    // std::unordered_set<MyClass, MyClass::Hash> set;

    // Add sample elements to the set
    set.emplace("key1");
    set.emplace("key2");

    // Check if the set contains "key1"
    if (set.contains("key1")) // Compile error on this line. Why does this not work?
    {
        std::wcout << L"set contains key1." << std::endl;
    } else {
        std::wcout << L"set doesn't contain key1." << std::endl;
    }

    return 0;
}

But I keep getting a compiler error on the call to contains() saying:

error: no matching function for call to ‘std::unordered_set<MyClass, MyClass::Hash, MyClass::Equal>::contains(const char [5])’ […]

I can’t figure out why, and cannot find any resource about it.

I guess I’m missing something, since the following code does work without issue using std::string:

int main()
{
    std::unordered_set<std::string> set;
   
    // Add sample elements to the set
    set.emplace("key1");
    set.emplace("key2");

    // Check if the set contains "key1"
    if (set.contains("key1")) // Why does this not work?
    {
        std::wcout << L"set contains key1." << std::endl;
    } else {
        std::wcout << L"set doesn't contain key1." << std::endl;
    }

    return 0;
}

>Solution :

Your issue is that set.contains("key1") is passing in a parameter that is not a MyClass object, which only works when both Hash::is_transparent and KeyEqual::is_transparent are valid and each denotes a type. This means you need to change your Hash and Equals structs to the following:

struct Hash
{
    using is_transparent = std::true_type;
    
    std::size_t operator()(const MyClass &class_) const
    {
        return std::hash<std::string>()(class_.m_name);
    }

    std::size_t operator()(const std::string &name) const
    {
        return std::hash<std::string>()(name);
    }

    std::size_t operator()(const char* name) const 
    {
        return std::hash<std::string>()(name);
    }
};

// Equal functor
struct Equal
{
    using is_transparent = std::true_type;
    
    bool operator()(const MyClass &lhs, const MyClass &rhs) const
    {
        return lhs.m_name == rhs.m_name;
    }

    bool operator()(const MyClass &lhs, const std::string &rhs) const
    {
        return lhs.m_name == rhs;
    }

    bool operator()(const std::string &lhs, const MyClass &rhs) const
    {
        return lhs == rhs.m_name;
    }

    bool operator()(const MyClass &lhs, const char* rhs) const
    {
        return lhs.m_name == rhs;
    }

    bool operator()(const char* lhs, const MyClass &rhs) const
    {
        return lhs == rhs.m_name;
    }
};

That will now inform std::unordered_set that it can use your hasher and comparator with objects other than the key type.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading