2
#include <iostream>
#include <bits/stdc++.h>
using namespace std;

void fun(const unordered_map<int, vector<int>>& direct_paths) {
    const int var = direct_paths[1][0];
    cout << var;
}

int main()
{
    unordered_map<int, vector<int>> a;
    a[1] = vector<int> {1,2,3};
    fun(a);
    return 0;
}

The above code outputs the following error:

error: passing ‘const std::unordered_map<int, std::vector<int> >’ as ‘this’ argument discards qualifiers [-fpermissive]
  const int var = direct_paths[1][0];
                                ^

Where as The below code doesn't output any compilation error:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;

void fun(const vector<int>& direct_paths) {
    const int var = direct_paths[1];
    cout << var;
}

int main()
{
    vector<int> a;
    a = vector<int> {1,2,3};
    fun(a);
    return 0;
}

Questions:

  1. Can I assign the value in a key-value pair of an unordered_map somehow?
  2. Why is assigning an integer from vector taken from const unordered_map<int, vector&> disallowed? & from const vector allowed?

Thanks in advance!

  • 1
    Try a more simple example of using [ ] on a `const map`. The problem is that it may create a new item in the map. Thus cannot be const. – Amir Kirsh Jun 29 '21 at 19:05
  • 1
    [Why should I not #include ?](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) and [Why is “using namespace std;” considered bad practice?](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – Some programmer dude Jun 29 '21 at 19:06

1 Answers1

6

The operator[] of a std::(unordered_)map is a non-const operator only, as it will modify the map to insert a new element if the requested key is not found. But inside of fun(), direct_paths is (a reference to) a const map object, so operator[] cannot be called on it. That is what the compiler is complaining about, as you can't call non-const methods on const objects.

The operator[] of std::vector has no such limitation, as it is overloaded to work with both const and non-const vector objects.

To fix the error you are seeing, you will need to use the map's at() or find() method instead, which can both be called on const map objects, eg:

void fun(const unordered_map<int, vector<int>>& direct_paths) {
    const int var = direct_paths.at(1)[0]; // will throw an exception if key '1' is not found...
    cout << var;
}
void fun(const unordered_map<int, vector<int>>& direct_paths) {
    auto iter = direct_paths.find(1); // will return the end() iterator if key '1' is not found...
    if (iter == direct_paths.end()) return; // or throw...
    const int var = iter->second[0];
    cout << var;
}
Kevin
  • 6,993
  • 1
  • 15
  • 24
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Is there a particular reason why accessing a nonexistent member through `vector::operator[]` is UB but `unordered_map` simply declines to have `const operator[]` at all? Desire to avoid UB, but not able to do so in `vector` because of commitment to mimicing array-like unchecked access through `operator[]`? – Nathan Pierson Jun 29 '21 at 19:11
  • "*Is there a particular reason why ...*" - ask the C++ standards committee. – Remy Lebeau Jun 29 '21 at 19:14
  • 1
    @NathanPierson A vector is basically "dynamic *array*". And arrays are a contiguous collection of elements, and `std::vector` implements the same semantics. Allowing any random index would break the array-like semantics. – Some programmer dude Jun 29 '21 at 19:23
  • @NathanPierson Not sure what you're suggesting might have been defined instead. Give `unordered_map` a const-qualified `operator[]` which has UB or throws on nonexistent key? Having const and non-const versions of something act so differently would be a dangerous violation of the Principle of Least Surprise. – aschepler Jun 29 '21 at 19:32
  • @aschepler UB on nonexistent key, analogous to `operator[]` in `vector`. I suppose non-`const` `operator[]` is already quite different from non-`const` `operator[]` in `vector` though since it's capable of performing insertions, so "make the containers' APIs more analogous" isn't very convincing. – Nathan Pierson Jun 29 '21 at 19:33
  • 2
    @NathanPierson my way of getting along with this inconsistency is to accept that `unordered_map::operator[]` is not element access, rather it is element access and insertion. Its a good reminder of why one needs to be careful with operator overloading ;) – 463035818_is_not_an_ai Jun 29 '21 at 19:56