Do STL maps initialize primitive types on insert?
operator[] looks like this:
Value& map<Key, Value>::operator[](const Key& key);
If you call it with a key that's not yet in the map, it will default-construct a new instance of Value, put it in the map under key you passed in, and return a reference to it. In this case, you've got:
map<wstring,int> Scores;
Scores[wstrPlayerName]++;
Value here is int, and ints are default-constructed as 0, as if you initialized them with int(). Other primitive types are initialized similarly (e.g., double(), long(), bool(), etc.).
In the end, your code puts a new pair (wstrPlayerName, 0) in the map, then returns a reference to the int, which you then increment. So, there's no need to test if the element exists yet if you want things to start from 0.
Is the value of primitive types in std::map initialized?
When invoking operator[]
and the key is missing, the value is initialized using the expression mapped_type()
which is the default constructor for class types, and zero initialization for integral types.
Why are entries of this STL map not being initialized in GCC 4.5.1?
As one would expect, the code excerpt is a simplified version of what's really going on. It turns out that a find command is used on the map, so that essentially map.find(1)->second
is being called, instead of simply map[1]
. This explains the undefined behavior.
In STL maps, is it better to use map::insert than []?
When you write
map[key] = value;
there's no way to tell if you replaced the value
for key
, or if you created a new key
with value
.
map::insert()
will only create:
using std::cout; using std::endl;
typedef std::map<int, std::string> MyMap;
MyMap map;
// ...
std::pair<MyMap::iterator, bool> res = map.insert(MyMap::value_type(key,value));
if ( ! res.second ) {
cout << "key " << key << " already exists "
<< " with value " << (res.first)->second << endl;
} else {
cout << "created key " << key << " with value " << value << endl;
}
For most of my apps, I usually don't care if I'm creating or replacing, so I use the easier to read map[key] = value
.
Does STL Map auto-initialize values?
Quoth the standard:
ISO/IEC 14882 §23.4.4.3
T& operator[](const key_type& x);
- Effects: If there is no key equivalent to
x
in the map, insertsvalue_type(x, T())
into the map.- Requires:
key_type
shall beCopyConstructible
andmapped_type
shall beDefaultConstructible
.- Returns: A reference to the
mapped_type
corresponding tox
in*this
.- Complexity: logarithmic.
So, not only is it guaranteed, but evaluating myMap["Hey"]
also causes the value 0 to be inserted into the map, if there was no entry for it before.
initialize part of a const std::map from another const std::map
While @Rakete111's answer is perfectly valid for what you need, I suggest elevating that to a function. Maybe you can use it multiple places.
std::map<int, std:::string> combine(std::map<int, std::string> const& map1,
std::map<int, std::string> const& map2)
{
std::map<int, std:::string> res(map1);
res.insert(map2.begin(), map2.end());
return res;
}
and then use
const auto secondMap = combine(firstMap,
std::map<int, std::string>{{3, "Third"}});
Force initialization to 0 when inserting into a std::map
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 thatstd::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.
std::map operator[] and automatically created new objects
The new object, when inserted into the map by []
operator, is value-initialized. It is ensured by the map implementation, i.e. it is done "automatically" in that sense. For objects of type DWORD
(assuming it is a scalar type), value-initialization means zero-initialization.
By definition given in 23.3.1.2, operator []
is a shorthand for
(*((insert(make_pair(x, T()))).first)).second
The T()
bit is the new object, which will turn into DWORD()
in your case. DWORD()
is guaranteed to be zero.
std::map default value for build-in type
This is defined in the standard, yes. map is performing "default initialization" in this case. As you say, for class types, that calls a no-arguments constructor.
For built-in types, in the '98 standard, see section 8.5, "Initializers":
To default-initialize an object of type T means:
- if T is a non-POD ...
- if T is an array type ...
- otherwise, the storage for the object is zero-initialized
And, previously,
To zero-initialize storage for an object of type T means:
- if T is a scalar type, the storage is set to the value 0 (zero) converted to T
Scalar types are:
- Arithmetic types (integer, floating point)
- Enumeration types
- Pointer types
- Pointer to member types
In particular, the behaviour you see with an integer (initialized to zero) is defined by the standard, and you can rely on it.
Related Topics
Does Std::Array<> Guarantee Allocation on the Stack Only
Proper Way of Casting Pointer Types
C++ Cannot Convert from Base a to Derived Type B via Virtual Base A
How to Define a Move Constructor
Are Mutex Lock Functions Sufficient Without Volatile
Overload Resolution and Arrays: Which Function Should Be Called
Using Opengl Glutdisplayfunc Within Class
C++ Object Instantiation VS Assignment
Using Continue in a Switch Statement
Why Gcc Does Not Use Load(Without Fence) and Store+Sfence for Sequential Consistency
C++ Const Keyword - Use Liberally
How to Use Makefiles in Visual Studio
How to Convert a String of Hex Values to a String
Best Way to Start a Thread as a Member of a C++ Class
Why Does the "Static" Keyword Have So Many Meanings in C and C++