tags:

views:

505

answers:

4

Is there a way to specify the default value std::map 's operator[] returns when an key does not exist?

Thanks!

+2  A: 

There is no way to specify the default value - it is always valeu constructed by the default (zero parameter constructor).

In fact operator[] probably does more than you expect as if a value does not exist for the given key in the map it will insert a new one with the value from the default constructor.

Michael Anderson
Right, to avoid adding new entries you could use `find` which does return the end iterator if no element exists for a given key.
Larry_Croft
+1  A: 

Maybe you can give a custom allocator who allocate with a default value you want.

template < class Key, class T, class Compare = less<Key>,
       class Allocator = allocator<pair<const Key,T> > > class map;
VDVLeon
`operator[]` returns an object created by invoking `T()`, no matter what the allocator does.
sbi
@sbi: Doesn't the map call the allocators `construct` method? It would be possible to change that, I think. I suspect a `construct` function that doesn something other than `new(p) T(t);` isn't well-formed, though. EDIT: In hindsight that was foolish, otherwise all the values would be the same :P Where's my coffee...
GMan
@GMan: my copy of C++03 says (in 23.3.1.2) that `operator[]` returns `(*((insert(make_pair(x, T()))).first)).second`. So unless I'm missing something, this answer is wrong.
sbi
You are right. But that seems wrong to me. Why dont they use the allocator function for inserting?
VDVLeon
@sbi: No, I agree this answer is wrong, but for a different reason. The compiler indeed does `insert` with a `T()`, but inside insert is when it will use the allocator get memory for a new `T` then call `construct` on that memory with the parameter it was given, which is `T()`. So it is indeed possible to change the behavior of `operator[]` to have it return something else, but the allocator cannot differentiate why it's being called. So even if we made `construct` ignore it's parameter and use our special value, that would mean *every* constructed element had that value, which is bad.
GMan
+2  A: 

The C++ standard (23.3.1.2) specifies that the newly inserted value is default constructed, so map itself doesn't provide a way of doing it. Your choices are:

  • Give the value type a default constructor that initialises it to the value you want, or
  • Wrap the map in your own class that provides a default value and implements operator[] to insert that default.
Mike Seymour
Well, to be precise the newly inserted value is value initialized (8.5.5) so:- if T is a class type with a user-declared constructor (12.1), then the default constructor for T is called(and the initialization is ill-formed if T has no accessible default constructor);— if T is a non-union class type without a user-declared constructor, then every non-static data member and baseclasscomponent of T is value-initialized;— if T is an array type, then each element is value-initialized;— otherwise, the object is zero-initialized
Tadeusz Kopec
+6  A: 

No, there isn't. The simplest solution is to write your own free template function to do this. Something like:

#include <string>
#include <map>
using namespace std;

template <typename K, typename V>
V GetWithDef(const  std::map <K,V> & m, const K & key, const V & defval ) {
   typename std::map<K,V>::const_iterator it = m.find( key );
   if ( it == m.end() ) {
      return defval;
   }
   else {
      return it->second;
   }
}

int main() {
   map <string,int> x;
   ...
   int i = GetWithDef( x, string("foo"), 42 );
}
anon
Nice solution. You might want to add a few template arguments so that the function template works with maps that don't use the default template parameters for comparator and allocator.
sbi
+1, but to provide the exact same behavior as the `operator[]` with default value, the default value should be inserted into the map inside the `if ( it == m.end() )` block
David Rodríguez - dribeas
@David I'm assuming the OP doesn't actually want that behaviour. I use a similar scheme for reading configurations, but I don't want the configuration updated if a key is missing.
anon
@David: I agree with Neil, but perhaps the best solution is having a parameter `doInsert` that defaults to false. When true it'll do the insert.
GMan
@GMan Boolean parameters? If the Brotherhood of API Purity get to hear about this, there'll be trouble!
anon
@Neil: :o!! Do explain :]
GMan
@GMan bool parameters are thought by some to be bad style because you can't tell by looking at the call (as opposed to the declaration) what they do - in this case does "true" mean "use default" or "don't use default" (or something else entirely)? An enum is always clearer but of course is more code. I'm in two minds on the subject, myself.
anon
@Neil: Oh I see, makes sense. I think I'm torn on it too, then.
GMan