1

Sorry for my inexperience with C++, but I spent quiet some time with solving a cyclic dependency issue and hence posing this.

I am trying to represent a Adjacency List in C++.

I have struct Node,

struct Node{

    int data;
    unordered_set<Node, Hash> links;

    bool operator == (Node const& other) const{
        return (data == other.data);
    }

    Node(){
    }

    Node(int data){
        this->data = data;
    }
};

and I have my Hash functor

struct Hash {
    size_t operator()(const Node &node) const {
        return node.data;
    };
};

I noticed that Hash uses Node and Node uses Hash
If for the purpose of this exercise I want to declare everything in a single file, which one should I declare first.

I tried forward declaration of both Hash and Node with defining either of them first, but none of them compiled.

PS: This is not homework, I'm trying to solve graph algorithm puzzles online

Anoop
  • 5,540
  • 7
  • 35
  • 52

2 Answers2

2

Delay defining Hash::operator() until after defining Node and declare Node before Hash. You can have a reference to an incomplete type as long as you don't do anything with it.

class Node;

class Hash{
    public:
        size_t operator()(const Node &node) const;
};

class Node{
    public:
        int data;
        unordered_set<Node, Hash> links;
};

inline size_t Hash::operator()(const Node &node) const{
    return node.data;
}
RamblingMad
  • 5,332
  • 2
  • 24
  • 48
  • 1
    Interestingly g++4.9/g++5 does not compile, complains about `Node` being an incomplete type, however clang++ does compile the code. – vsoftco Mar 07 '15 at 03:13
  • @vsoftco I think I saw a question on SO about this difference, and trying to figure out which of the two compilers is right, but I can't seem to find it, and I don't remember what was the answer. – Sergey Kalinichenko Mar 07 '15 at 03:29
  • @dasblinkenlight it seems the above code is UB, I just asked a question about this http://stackoverflow.com/q/28911009/3093378 – vsoftco Mar 07 '15 at 03:32
  • @vsoftco [this question/answer](http://stackoverflow.com/questions/7356603/references-to-incomplete-types) seems to suggest otherwise. – RamblingMad Mar 08 '15 at 06:35
  • @CoffeeandCode I think the issue was clarified in the C++11 standard, not sure about C++03. – vsoftco Mar 08 '15 at 06:51
1

Resolving the syntax by moving the implementation of hash to a point after the Node is fully defined is insufficient. No matter what the order is, you would not be able to compile it, because unordered_set of Node expects the Node to be a complete type, i.e. the type needs to be fully defined.

In addition to splitting out the definition of Hash::operator() you need to change the first type parameter of the unordered_set to a pointer, preferably a smart pointer:

unordered_set<shared_ptr<Node>, Hash> links;
...
size_t Hash::operator()(const shared_ptr<Node> &node) const{
    return node->data;
}

A regular pointer would also work, but then you would have to manage memory for your nodes separately - e.g. by placing all nodes in a vector.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523