I am confused regarding the behavior of the following code: I don't understand why the getNodeSuccess
method works, but the getNodeFail
method fails to build. The only difference between those two methods is that one uses the []
operator for accessing the map, and the other one the at
method. I thought that both these operations were returning a reference to the element in the map, and both should basically do the same thing. However, when I use the []
operator instead of the at()
method, it tries to call the default constructor of the GraphNode class. Since the default constructor is private, the build fails.
For demonstration, I've made the default constructor of the GraphNode
class private. If I change it to public, than it works.
Can somebody explain what's going on here? Why does one method trigger the default constructor, and not the other one?
#include <vector>
#include <list>
#include <unordered_map>
#include <exception>
template <class T>
struct GraphNode
{
int id;
T data;
std::list<int> adj; // adjacency list
GraphNode(const int id)
{
this->id = id;
}
private:
GraphNode() {}
};
template<class T>
struct UndirectedGraph
{
std::unordered_map<int, GraphNode<T>> lookup;
std::vector<GraphNode<T>> vertices;
void addNode(const GraphNode<T> node)
{
// if the id of the node already exists, than we cannot add it
if (lookup.find(node.id) == lookup.end())
lookup.insert(std::make_pair(node.id, node));
else
throw std::runtime_error("Trying to add a node that already exists.");
}
void addEdge(const int source, const int destination)
{
getNodeSuccess(source).adj.push_back(destination);
getNodeSuccess(destination).adj.push_back(source);
getNodeFail(source).adj.push_back(destination);
getNodeFail(destination).adj.push_back(source);
}
GraphNode<T>& getNodeSuccess(const int id)
{
if (lookup.find(id) == lookup.end())
throw std::runtime_error("Trying to retrieve a node that doesn't exist");
return lookup.at(id);
}
GraphNode<T>& getNodeFail(const int id)
{
if (lookup.find(id) == lookup.end())
throw std::runtime_error("Trying to retrieve a node that doesn't exist");
return lookup[id];
}
};
int main(int argc, char* argv[])
{
UndirectedGraph<int> graph;
graph.addNode(GraphNode<int>(1));
graph.addNode(GraphNode<int>(3));
graph.addEdge(1, 3);
return 0;
}
operator[]
will either return
a reference to the value with that key, or create one using the default constructor, insert it, and return
a reference to the newly constructed value. Thus, your problem is that operator[]
must be able to at least call the default constructor, even if you know the key will always be present.
If you just want to lookup whether something is in your unordered_map
, your use of find
is already close.
GraphNode<T>& getNodeFail(const int id)
{
auto it = lookup.find(id);
if (it == lookup.end())
throw std::runtime_error("Trying to retrieve a node that doesn't exist");
return it->second;
}
That will save you an extra lookup as well, since you already did the work once with find
.