0

I've run into certain situations where an unordered_map can't be passed into a function as const &, whereas in other situations this seems to be allowed. I'm unable to make sense of the errors in the cases where a compiler won't allow this. For example, consider this example program to count the number of connected components in a graph:

// NumConnectedComponents.cpp

#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>

bool setContainsItem(const std::unordered_set<int>& S, int item)
{
  if (S.find(item) == S.end()) return false;
  else return true;
}

void countNumConnectedComponents2(int currentNode, const std::unordered_map<int, std::vector<int>>& graph, std::unordered_set<int>& visited)
{
  visited.insert(currentNode);

  std::vector<int> adjNodes = graph[currentNode];

  for (auto& adjNode : adjNodes)
  {
    if (!setContainsItem(visited, adjNode))
    {
      countNumConnectedComponents2(adjNode, graph, visited);
    }
  }
}

int countNumConnectedComponents(int numNodes, const std::unordered_map<int, std::vector<int>>& graph)
{
  std::unordered_set<int> visited;

  int numConnComps = 0;

  for (int i = 0; i < numNodes; i++)
  {
    if (!setContainsItem(visited, i))
    {
      countNumConnectedComponents2(i, graph, visited);
      numConnComps++;
    }
  }

  return numConnComps;
}

int main(void)
{
  int numNodes = 5;

  std::unordered_map<int, std::vector<int>> graph;

  for (int i = 0; i < numNodes; i++)
  {
    graph[i] = std::vector<int>();
  }

  // add some edges

  graph[0].push_back(3);
  graph[3].push_back(0);

  graph[1].push_back(2);
  graph[2].push_back(1);

  graph[4].push_back(2);
  graph[2].push_back(4);


  int numConnComps = countNumConnectedComponents(numNodes, graph);

  std::cout << "\n" << "numConnComps = " << numConnComps << "\n\n";

  return 0;
}

If I enter this program as above, on the line:

std::vector<int> adjNodes = graph[currentNode];

From the Visual Studio C++ compiler I get the error:

Error C2678
binary '[': no operator found which takes a left-hand operand of type 'const std::unordered_map<int,std::vector<int,std::allocator<int>>,std::hash<int>,std::equal_to<int>,std::allocator<std::pair<const int,std::vector<int,std::allocator<int>>>>>' (or there is no acceptable conversion)

In Ubuntu the gcc complier gives a similar error.

If I remove const from the function signatures for countNumConnectedComponents2 and countNumConnectedComponents (and leave everything else exactly the same) the program runs fine.

Can anybody explain this error, or what can be done to resolve it other than removing const? I'd really like to use const when passing an unordered_map between functions if it should not be modified, and I need to retain the & to avoid passing around copies of large maps.

Additionally I can't make rhyme or reason out of why in other situations const is allowed for unordered_map parameters, for example this program compiles and runs fine:

// SumUMapValues.cpp

#include <iostream>
#include <unordered_map>

int sumValsInMap(const std::unordered_map<int, int>& umap)
{
  int sum = 0;
  for (auto& element : umap)
  {
    sum += element.second;
  }
  return sum;
}

int main(void)
{
  std::unordered_map<int, int> umap;

  umap[1] = 10;
  umap[2] = 20;
  umap[3] = 30;

  int sumOfVals = sumValsInMap(umap);

  std::cout << "\n" << "sumOfVals = " << sumOfVals << "\n\n";
}
cdahms
  • 3,402
  • 10
  • 49
  • 75
  • 1
    The `operator[]` function requires the object being operated upon to be non-const since it may insert an object (hence modifying the map). Use `unordered_map::at` instead of `unordered_map::operator[]`. – JohnFilleau Oct 04 '20 at 20:07
  • Thanks, changing the line with the error to "const std::vector adjNodes = graph.at(currentNode);" does resolve it. That's really silly though, the [] operator is what everybody would expect to use if I was writing such a function to be used by somebody else in the future. I'm not sure why the C++ people couldn't make [] work with const or non-const. – cdahms Oct 04 '20 at 20:09
  • See the dupe, it addresses your comment as well. – cigien Oct 04 '20 at 20:12
  • @cdahms How would `[]` work on a `const` map if you try to access an element that doesn't exist? – super Oct 04 '20 at 20:14
  • C++ has a big emphasis on getting the types right. You'll find this really annoying when writing toy code, but extremely helpful for squishing bugs once the programs start getting complicated. Everything the compiler picks of is one less runtime error you need to find and fix. – user4581301 Oct 04 '20 at 20:17

0 Answers0