Search code examples
c++stdvectorstdmap

Why I can't use the std::map[ ] to add a string, but std::map.at() works?


My problem is why the s += t.getM()[0]; in the example code raise

main.cpp:44:20: error: passing ‘const std::map >’ as ‘this’ argument discards qualifiers [-fpermissive]

I checked the cppreference and it says both return a reference.

In addition, why both operator[] and .at() work for std::vector?

Example code is here.

#include <iostream>
#include <vector>
#include <map>
#include <string>

using namespace std;

class test {
    public:
        test(string str) {
            vec.push_back(str);
            mp[0] = str;
            
        }   
        
        const vector<string>& getV() const {
            return vec;
        }
        
        const map<int, string>& getM() const {
            return mp;
        }
        
    private:
        vector<string> vec;
        map<int, string> mp;
};

int main()
{
    string s;
    test t("hello ");
    s += t.getV().at(0);
    s += t.getV()[0];
    s += t.getM().at(0);
    s += t.getM()[0];
    cout << s;
}

Solution

  • std::map::operator[] only works for a non-const std::map. The documentation on std::map::operator[] explains this pretty well. Here's an excerpt from the beginning of the page:

    Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist.

    As you can see, if the key doesn't exist, it inserts a new key/value pair into the map. Obviously, this wouldn't work for const maps because you can't insert elements into them since they're immutable. Why there isn't a const overload for the operator that will not create a new value, I don't know. But that's just how it is.

    std::map::at(), however doesn't work exactly like std::map::operator[]. Again, an excerpt from the documentation of std::map::at():

    Returns a reference to the mapped value of the element with key equivalent to key. If no such element exists, an exception of type std::out_of_range is thrown.

    Furthermore, the function has a const overload too: const T& at(const Key& key) const; So it can work for const maps.


    In addition, why both operator[] and .at() work for std::vector?

    Because std::vector::operator[] and std::vector::at() work very similarly, except std::vector::at() does bounds checking while std::vector::operator[] doesn't. Neither will create a new value (because that's just not how vectors work) and both have const overloads. In fact, the documentation for std::vector::operator[] even addresses the difference between it and std::map::operator[]:

    Unlike std::map::operator[], this operator never inserts a new element into the container. Accessing a nonexistent element through this operator is undefined behavior.

    (It's undefined behavior because, as I previously mentioned, operator[] does no bounds checking.)