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"; } 
5
  • 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[]. Commented Oct 4, 2020 at 20:07
  • Thanks, changing the line with the error to "const std::vector<int> 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. Commented Oct 4, 2020 at 20:09
  • See the dupe, it addresses your comment as well. Commented Oct 4, 2020 at 20:12
  • @cdahms How would [] work on a const map if you try to access an element that doesn't exist? Commented Oct 4, 2020 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. Commented Oct 4, 2020 at 20:17

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.