Search code examples
c++unordered-mapdefault-constructor

Explicitly defaulted default constructor is implicitly deleted because of unordered_map used with a struct as key


I have the following Graph class:

class Graph {
private:
    struct Edge {
        string vertex1{}, vertex2{};
        int val{};

        Edge() = default;
        ~Edge() = default;
        explicit Edge(string v1, string v2, int value) : vertex1(std::move(v1)), vertex2(std::move(v2)), val(value) {};
        bool operator==(const Edge&) const;
    };

    unordered_map<Edge, Edge*> edges;

public:
    Graph() = default;
    ~Graph();
}

When I want to construct a Graph with the default constructor, it says Explicitly defaulted default constructor of 'Graph' is implicitly deleted because field 'edges' has a deleted default constructor. How should I change my code, to be able to use the default constructor of Graph?


Solution

  • The Key in an unordered_map needs to be hashable. Either by adding a specialization of the std::hash class template or by providing a hashing functor when creating the unordered_map.

    You haven't shown that you've made a std::hash<Edge> specialization and you aren't creating the unordered_map with a functor to do the hashing which is why default construction fails.

    To add a std::hash<Edge> specialization you could do this (if you make Edge public):

    namespace std {
    
    template<>
    struct hash<Graph::Edge> {
        size_t operator()(const Graph::Edge& e) const {
            size_t hash_result;
            // calculate the hash result
            return hash_result;
        }
    };
    
    } // namespace std
    

    If you'd like to keep Edge private it may be easier to use a functor. Here's an example in the form of a struct which makes the unordered_map default constructible:

    class Graph {
    private:
        struct Edge {
            // ...
        };
    
        // the hashing functor:
        struct edge_hasher {
            size_t operator()(const Edge& e) const {
                // an example implementation using boost::hash_combine
                // from <boost/container_hash/hash.hpp>:
                std::hash<std::string> h;
                size_t hash_result = 0;
                boost::hash_combine(hash_result, h(e.vertex1));
                boost::hash_combine(hash_result, h(e.vertex2));
                boost::hash_combine(hash_result, std::hash<int>{}(e.val));
                return hash_result;
            }
        };
    
        std::unordered_map<Edge, Edge*, edge_hasher> edges; // <- hasher included
    
    public:
        Graph() = default;
        ~Graph();
    }