tags:

views:

233

answers:

3

I tried to pass const with vector it works: Ex:

void damn(const vector <bool> &bb)
{
    for (int i=0; i<bb.size(); i++)
        cout<<bb[i]<<endl;

}

But when trying with map, it does not:

void pas(const map <string, float> &mm)
{
    cout<<mm["a"];
    cout<<mm["b"];
}

I wonder why it doesn't.

+5  A: 

std::map::operator[] inserts a default-constructed element if the requested element is not in the map. This is why it is not a const member function. You can use std::map::find instead, but be sure to check the iterator it returns.

bk1e
+19  A: 

map::operator[] is a little odd. It does this:

  1. Look for the key.
  2. If found, return it.
  3. If not, insert it and default-construct its associated value.
  4. Then return a reference to the new value.

Step 3 is incompatible with constness. Rather than have two differently-functioning operator[] overloads, the language forces you to use map::find for const objects.

Alternately, one could argue, what would map::operator[] const do if the argument is not in the map? Throw an exception? Undefined behavior? (After all, that's what vector::operator[] does with an index out of bounds.) In any case, the problem is avoided with only a small inconvenience to us.

my_map.find(key) returns my_map.end() if the key is not found.

Potatoswatter
To be fair to the standard library - how would operator[] const behave? What would it do if the element did not exist? Having it throw an exception would be confusing because none of the other standard containers behave this way. In my opinion they should have solved this question by leaving the non-const operator[] out, but that is just my opinion.
Stewart
@Stewart: The most uniform syntax and semantics would result from reversing the roles of `vector::at` and `vector::operator[]`, and defining `map::operator[] const` to throw. But that would hurt performance too much. Given the suggestion on this page to do `cout<<my_map.find(key)->second;`, I'm currently favoring the exceptions solution. Surprising as they may be, at least they're informative when they pop up.
Potatoswatter
@Potatoswatter: I see your point, but they could't have changed vector's operator[] semantics because that would make it incompatible with arrays. I've personally never used vector::at and i've never seen anyone use it - I always make sure I know when my position is valid instead. Swapping the roles would mean I would have to use at() everywhere, as would any code i've ever seen that uses vector.
Stewart
@Stewart: Accessing out of bounds indices isn't a feature required for compatibility. `at()` and `[]` are interchangeable for valid indexes, so I don't see your point. The crucial difference is in performance. Actually, to *really* make `vector` and `map` match up, an out-of-bounds vector subscript should call `resize`. Doesn't Java do it that way? Anyway, it's a pointless argument since C++ is designed to be high-performance at the cost of ease.
Potatoswatter
@Potatoswatter - I wasn't arguing - just enjoying the debate :). For me, I use C++ for operating systems development and if std::vector didn't behave the way it does we'd still be using C for pretty much everything. It was one of the first "proper" C++ things we started using in earnest and opened the door to much more - the range checking would be a perf hit that the old school C guys would not have allowed. I don't know about Java, but I know that C# always does range checking. This is one of the reasons iteration is more performant.
Stewart
@Stewart: agreed 100%. In the big picture, C++ has occasional rough edges like this one, but in each case you learn a little and move on, and end up chugging up that slope all the same.
Potatoswatter
+1  A: 

I believe that it is because [] in map isn't const, as it creates new pair with default value, if you address to nonexisting one. Try

void pas(const map <string, float> &mm)
{
    cout<<mm.find("a")->second;
    cout<<mm.find("b")->second;
}
Draco Ater
Make that `cout << mm.find("a")->second`, since mm.find("a") returns an iterator pointing to a std::pair.
Joey Adams
Joe Adams, thank you. It solved my problem.
tsubasa
Note that such code is likely to crash if the key isn't found.
Potatoswatter
You *must* check the returned iterator if you do this - find will return map::end() if the element is not present, and dereferencing map::end() is undefined behaviour.
Stewart