Search code examples
c++c++11stdmapallocatorsize-t

Force initialization to 0 when inserting into a std::map


I have a std::map<std::string,std::size_t> of keys mapping counters. When I increment a counter, I don't know if it already exists. If yes, it is incremented. If not, it is set to 1. This is easy to do:

std::map<std::string,std::size_t> counters;
//...
auto c_it = counters.find(key);
if(c_it == counters.end())
  counters.insert(key,1);
else
  (*c_it)++;

This is a lot of code for a simple thing... I want to do this:

counters[key]++;

But this generates undefined behavior because std::size_t() is called when the counter doesn't exist in the map, and there is no guarantee that std::size_t() initializes to 0.

I see two potential solutions:

  1. Find / create a type similar to std::size_t forcing initialization to 0 when created with default constructor. Is there such a type in std or boost ?
  2. Find / create a dedicated allocator to replace the default std::allocator<std::pair<const Key,T>> given as 4th template argument of std::map. But I have no idea how to do it.

Note: I use C++11 only (I don't want solutions for C++>=14)


Solution

  • But this generates undefined behavior because std::size_t() is called when the counter doesn't exist in the map, and there is no guarantee that std::size_t() initializes to 0.

    This assumption is incorrect. Quote from cppreference:

    If an insertion is performed, the mapped value is value-initialized (default-constructed for class types, zero-initialized otherwise) and a reference to it is returned.

    The quote is for pre-C++11 version, but it's easier to understand that the tuple magics and the observable effect is the same.

    The value will be zero-initialized, you can use counters[key]++; without issues.