0

For my small program, I need a class representing a node in an undirected graph. It looks like this:

#ifndef DIRECTED_GRAPH_NODE_H
#define DIRECTED_GRAPH_NODE_H

#include <cstdlib>
#include <string>
#include <ostream>
#include <unordered_set>
#include <utility>

class DirectedGraphNode;

class DirectedGraphNode {
private:
    std::size_t m_id;
    std::unordered_set<DirectedGraphNode*, 
                       DirectedGraphNodeHasher,
                       DirectedGraphNodeEqualTo>* m_neighbors;

public:
    DirectedGraphNode(std::size_t id) 
        : m_id{ id } 
        , m_neighbors{ new std::unordered_set<DirectedGraphNode*>} {}

    std::unordered_set<DirectedGraphNode*, 
                       DirectedGraphNodeHasher,
                       DirectedGraphNodeEqualTo>* getNeighbors() {

        return m_neighbors;
    }

    void addNeighbor(DirectedGraphNode& neighbor) {
        m_neighbors->insert(&neighbor);
    }

    bool operator==(DirectedGraphNode* other) {
        return m_id == other->m_id;
    }

    friend std::ostream& operator<<(std::ostream&, const DirectedGraphNode&);
    friend class DirectedGraphNodeHasher;
    friend class DirectedGraphNodeEqualTo;
};

std::ostream& operator<<(std::ostream& os, const DirectedGraphNode& node) {
    return os << "[DirectedGraphNode: id = " << node.m_id << "]";
}
struct DirectedGraphNodeHasher {
    std::size_t operator()(const DirectedGraphNode*& node) const {
        return node->m_id;
    }
};

struct DirectedGraphNodeEqualTo {
    bool operator()(const DirectedGraphNode*& node1, const DirectedGraphNode*& node2) const {
        return node1->m_id == node2->m_id;
    }
};

#endif // DIRECTED_GRAPH_NODE_H

The above gives me a plethora of error messages. I am clueless how to resolve this. I tried the following approaches:

class A;

class B {
private:
    A* pa;
}

class A {
private:
    B* pb;
}

and this:

class B;

class A {
private:
    B* pb;;
}

class B {
private:
    A* pa;
}

What comes to DirectedGraphNodeHasher and DirectedGraphNodeEqualTo, I need them in order for the actual graph nodes to play nicely with the std::unordered_set<DirectedGraphNode*>.

Needless to say, none of the above compiles. What am I doing wrong here?

6
  • 3
    Don't define all your methods inside your classes. That inevitably means you are using one class before it has been defined. Define all constructors and methods after both classes have been defined. Commented Jun 20, 2024 at 10:34
  • 1
    In other languages like java you declare and implement the classes in the same file, while in c++ we split classes declaration in a header .h file and an implementation .cpp file, this solves some of the circular dependencies, but a better solution is to not have circular dependencies to begin with. Commented Jun 20, 2024 at 10:43
  • Why are you forward declaring Node and the next line defining the class? Forward declaration is a really powerful tool and can be used to resolve this cycle: en.cppreference.com/w/cpp/language/class Commented Jun 20, 2024 at 10:45
  • 2
    both variants of "I tried the following" do work. They have no errors (missing ; aside). Commented Jun 20, 2024 at 10:48
  • Please show an actual file that failed to compile for you, along with actual compiler messages. Do simplify your example as much as possible, but no more than that. When the simplified example starts to compile, you've gone too far. Commented Jun 20, 2024 at 10:54

1 Answer 1

3

You need to put both the hasher and the equality classes inside your node class.

class node
{
public:
    class hasher { ... };
    class equality { ... };
    std::unordered_set<node, hasher, equality> children;
};

This allows hasher and equality to use node and allows node to use them both without needing to forward declare the classes.


The other alternative is to forward declare the classes. Your node class requires hasher and equality to be complete, while hasher declaration doesn't need node to be complete, so hasher can be declared before node.

class node; // forward declare node

class hasher
{
    // function declaration doesn't need node to be complete
    size_t operator()(const node&) const;
};

class node
{
    // hasher is complete here
    std::unordered_set<node, hasher, equality> children;
};

// later in header or in a .cpp file
// inline needed if this is a header file
inline size_t hasher::operator()(const node&) const
{
 // use node here as it is now complete
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.