Search code examples
c++c++11unique-ptrunordered-set

Unable to find a user-defined type in a c++ unordered set with custom operator==()


Problem Statement: Iterate over an array of objects and check if the object exists in an unordered_set.

Goal: I could have thousand of objects in one container to check their existence in millions of objects in another container. I choose unordered_set for its constant finding complexity and vector for iterating. I'm new to this and if you have any alternate approach, I'd really appreciate it.

Issue: unordered_set find isn't working as expected or I got the concept wrong!

Main:

int main() {
  std::vector<std::unique_ptr<Block>> vertices;

  vertices.push_back(std::make_unique<Block>("mod1", "work"));
  vertices.push_back(std::make_unique<Block>("mod2", "work"));
  vertices.push_back(std::make_unique<Block>("mod3", "work"));

  std::unordered_set<std::unique_ptr<Block>> undefs;

  undefs.insert(std::make_unique<Block>("mod1", "work"));
  undefs.insert(std::make_unique<Block>("mod2", "work"));

  for(auto& vertex : vertices) {
    auto search = undefs.find(vertex);
    if(search != undefs.end()){
      std::cout << "Block: " << vertex->getName() << "\n";
    }
  }
}

Block Class Overload:

bool Block::operator==(std::unique_ptr<Block>& block) const {
  return block->getName() == mName;
}

Expected Output:

mod1

mod2

Block:

#pragma once

#include <string>
#include <memory>

using std::string;

class Block {
  private:
    string mName;
    string mLib;
  public:
    Block(string const& name, string const& lib);
    string getName() const;
    string getLib() const;
    bool operator==(std::unique_ptr<Block>& block) const;
};

Solution

  • You are trying to compare pointers, not values. You need to specify hashing function for class Block.

    For example, if you want to use mName as key the code will be the following:

    class Block {
    private:
        string mName;
        string mLib;
    public:
        Block(string const& name, string const& lib)
        {
            mName = name;
            mLib = lib;
        }
        string getName() const {
            return mName;
        };
        string getLib() const {
            return mLib;
        }
        bool operator==(const Block & block) const;
    };
    
    template<> struct std::hash<Block> {
        std::size_t operator()(const Block & block) const noexcept {
            return std::hash<std::string>{}(block.getName());
        }
    };
    
    bool Block::operator==(const Block & block) const {
        return block.getName() == mName;
    }
    
    int main() {
        std::vector<Block> vertices;
    
        vertices.emplace_back(Block("mod1", "work"));
        vertices.emplace_back(Block("mod2", "work"));
        vertices.emplace_back(Block("mod3", "work"));
    
        std::unordered_set<Block> undefs;
        undefs.emplace(Block("mod1", "work"));
        undefs.emplace(Block("mod2", "work"));
    
        for (auto& vertex : vertices) {
            auto search = undefs.find(vertex);
            if (search != undefs.end()) {
                std::cout << "Block: " << vertex.getName() << "\n";
            }
        }
    }