Chuan Xu
Chuan Xu

Reputation: 37

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;
}

Upvotes: 0

Views: 1335

Answers (2)

Arnab De
Arnab De

Reputation: 462

Just to add to @mediocrevegetable1's answer above, the following increases the size of the container:

class test {
    public:
           ...
           map<int, string>& getM() {
               return mp;
           }
    private:
           ...
}

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;
    cout << t.getM().size() << endl;  // prints 1
    auto temp = t.getM()[1]; // Key=1 does not exist in the container and hence is added
    cout << t.getM().size() << endl; // prints 2
}

Also, I got better hint regarding the issue from clang instead of gcc:

no viable overloaded operator[] for type 'const map<int, std::string>' (aka 'const map<int, basic_string<char> >')

Upvotes: 0

mediocrevegetable1
mediocrevegetable1

Reputation: 4217

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.)

Upvotes: 1

Related Questions