C++ unordered_map/map [] operator default initialization value [duplicate] - c++

I have a std::map like this:
map<wstring,int> Scores;
It stores names of players and scores. When someone gets a score I would simply do:
Scores[wstrPlayerName]++;
When there is no element in the map with the key wstrPlayerName it will create one, but does it initialize to zero or null before the increment or is it left undefined?
Should I test if the element exists every time before increment?
I just wondered because I thought primitive-type things are always undefined when created.
If I write something like:
int i;
i++;
The compiler warns me that i is undefined and when I run the program it is usually not zero.

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.

This will default-construct a new instance of value. For integers, the default construction is 0, so this works as intended.

You should not test if the item exists before incrementing it. The [] operator does exactly what you need it to do, as others have said.
But what if the default-constructed value wouldn't work for you? In your case the best way to find if the element already exists is to try to insert it. The insert member function for std::map returns a std::pair<iterator, bool>. Whether the insert succeeds or fails, the first element of the pair will point to the desired object (either your new one, or the one that was already present). You can then alter its value as you see fit.

Check rules for initialization.
See section 4.9.5 Initialization of C++ Prog Lang or C++ std book. Depending on whether your variable is local, static, user-defined or const default initialization can happen.
In you case, int is called POD (Plain old Datatype). Any auto (created on heap / local variable) POD variable is not default initialized. Hence for you "i" above will not have value zero.
Always make an habit of initializing POD when defined in heap. You can even use int() to initialize value.

Related

C++ map :first call to map[a], how to initialize map[a].second to zero?

When I use operator[] to call map[a]; while there is no key a in the map will the second element in map[a]be initialized to zero? If not, are there any good way to do it? I'm currently using if (map.find(a)) map[a]++; else map[a] = 1;. I want to use a map that is initialized to 0 and calling only map[a]++; and map[a]--;
Assuming a map with a type such as std::map<int, int> m, then a call to m[a] for a non-existent key a will insert an element with key a and value int(), that is, 0. So you can safely simplify your code to
std::map<int, int> m
....
m[a]++; // OK
In the general case, the new element insertion inserts a value initialized key object to the map. For built-in arithmetic types such as int, value initialization means zero initialization.
Assuming a std::map, if operator[] creates a new element (i.e. for a key that doesn't previously exist, then the mapped value is value initialised.
For basic types like int, that means a value of zero. For class/class types, it means default construction.

Passing reference to deque delete function

I have been given an assignment and I'm struggling to figure out how I'm supposed to implement it.
I've pasted the parts of the assignment that puzzled me below
Write a deque class to hold a list of integers which is implemented internally with a circular array The size of the array can be passed in the constructor, or you can decide on a default value. The class will maintain data members which hold the index position of the head and tail of the list
The class should have member functions:
• bool isEmpty();
• bool isFull();
• bool insertFront(int)
• bool removeFront(int&)
• bool insertBack(int)
• bool removeBack(int&)
prints all items in the array by removing them one at a time from the front.
So I've written all my function and have the deque working, the things I struggled with are:
"The size of the array can be passed in the constructor"
so to accomplish this I declared a pointer called array in my class and then array = new int[size] in my constructor, is this the only way to do this, I'm happy enough it works but not sure if there's a better solution. I was thinking vector, but think that would have been too easy. I also could have declared a const for the size and initialized the array in my class, but again to easy.
The bool removeFront(int&) and bool removeBack(int&) functions really confused me, what reference am I supposed to be passing in? also the return type is bool, but later in the assignment I'm asked to "prints all items in the array by removing them one at a time from the front" how can I do this with a return type of bool, rather than int?
I have changed my functions to remove the reference and have a return type of int to get the code to work, but would like to know how to implement it the way the assignment asks for?
Based on the requirements listed, the intent of the function arguments is unambiguous. Here is why:
Take
bool removeFront(int& );
This not only removes an element at the front of the buffer and stores it in the argument being passed by reference. But, the function returns a "bool" indicating whether it was able to successfully remove or not.
An example usage would be like this:
int elem;
while (removeFront(elem)) {
printf("element : %d ", elem);
}
Here the variable "elem" is passed in by reference. Hence, upon a successful execution of removeFront() you will have elem filled in with the value of the element just removed.
The same reasoning applies to other similar methods. Please go back to using a reference mode parameter as given in the original specification.
The int& argument is not for a count of elements as other answer suggested.
Answer to Part-1:
Your solution is pretty decent. You could also
std::array for storing the elements. There is an advanced trick to do in-place allocation of a variable length array - but, that is beyond the scope of this question.
"The size of the array can be passed in the constructor"
Unless you were told otherwise, use a vector. Using old school arrays is just asking for trouble.
The "bool removeFront(int&)" and "bool removeBack(int&)" functions really confused me, what reference am I supposed to be passing in?
It's a matter of personal preference, but passing in a single int as a reference might be rather unnecessary, what the functions do (if I understood your problem correctly) is remove the element of the array that is at the position of the int you are passing as argument. If said element is correctly removed, you might want to return a true value, otherwise return a false one.
EDIT: Upon re reading the post, what the functions might do is simply remove the 'int' amount of elements from the front or back of the array. Return values should work as previously stated
but later in the assignment I'm asked to "prints all items in the array by removing them one at a time from the front" how can I do this with a return type of bool, rather than int?
The return type of the function has nothing to do with this (unless you were asked to do it recursively). Simply do a loop that starts at the beginning of the array and outputs its content, deletes that same element, then jumps to the next and repeats the process until its out of elements. Again, this is much safer to do with any of the STL containers since you can use iterators.

Safe to use += operator to create a new std::map entry using []?

Let's say I have a std::map<int, int>, would it be safe to do this?
std::map<int, int> m_map;
m_map[0] += 1;
If the key 0 didn't exist in the map when I do this, how would it know what value to add 1 to?
What I'm hoping is that std::map handles this by performing an = instead of += in the case where the value creates a new entry in the map. This would spare me from having to do:
std::map<int, int>::iterator it = m_map.find(0);
if(it != m_map.end()) {
it->second += 1;
}
else {
m_map[0] = 1;
}
An element inserted into a map on invoke of operator[] due to a previously-unmapped key is value-initialized. If you've not seen that syntax, consider the special meaning the () has in the following code snippet. The parens are important. They introduce an different trip down the initialization tree than default-initialization. Both are important; both are laid out by the language standard.
int i = int();
As it turns out, value initialization for scalars (including pointers) eventually succumbs to zero-initialization. Though odd looking, the prior snippet value-initializes an instance of int, which becomes zero-initialization since int is a scalar, then copies it to i. (To be fair, there will almost certainly be some eliding, but the fundamentals are as-presented).
Regardless, due to that feature, you can rest assured when you do this:
m_map[0] += 1;
or even this:
++m_map[0];
if the index wasn't mapped prior, a value-initialized element is added, which will zero-initialize for scalars, which means you'll officially starting out with zero.
It is worth mentioning that a similar activity happens for any type with an implicitly declared constructor. Whether trivial or not, something interesting happens.
struct S { int a; int b; };
std::map<int, S> mymap;
++mymap[0].a;
Is the a member mapped to 0 in our container reliably 1 after the above executes? Yes, it is. Further, consider this:
struct S { int a; std::string str; };
std::map<int, S> mymap;
++mymap[0].a;
Now S has a non-trivial implicit constructor (it has to, as it must construct str). But is the a member mapped to 0 in our container still reliably zero-initialized (and therefore 1 after the above line)? Yes, it is.
If curious about the referenced different paths of initialization, see this question and answer. Or review the C++11 standard, particularly C++11 § 8.5 Initializers, (p5,p7,p10). It is worth the read.
It's guaranteed that you'll get 1 even with the shorter snippet.
The key is that operator[] has to create the element in the map before returning a reference to it, so by the time you got to += the element already exists, and, if it had to be created now, with value zero (since elements of a map are value-initialized).
(incidentally, this is the reason why, when you use an std::map with a class type as value, it has to have a default constructor if you want to use operator[], even if you assign an object to it immediately)
Yes this is fine, new entries default to value value_type() which is 0 for int.
It still does +=, but 0 += 1 gives 1.
Map is lazily initialize with default constructor.(zero for all int)
So it 0 didn't exist in map, it would be initialized to contain value 0 and 1 would be added after += 1.
So you can use upper version of code without any worries.
Quoting Effect of calling map_obj[k] from cplusplus.com
If k matches the key of an element in the container, the function
returns a reference to its mapped value.
If k does not match the key of any element in the container, the
function inserts a new element with that key and returns a reference
to its mapped value. Notice that this always increases the container
size by one, even if no mapped value is assigned to the element (the
element is constructed using its default constructor).

std::map<string,int> default initialization of value [duplicate]

This question already has answers here:
std::map default value for build-in type
(4 answers)
Closed 6 years ago.
This piece of code seems work well, with default value for they value_type (int) as 0; does it work for all cases?
std::map<std::string,int> w;
for (const auto& t: str)
w[t]++;
What about double?
map? default 0.0?
Yes, this code would work for any type of the key, including double. The reason this works is that the non-const operator [] returns a reference to the value at the key, not a copy of that value. It is that reference to which the ++ operator gets applied.
The code fragment that you show works as follows:
For each key t of type string in the str container,
The map w is searched for the given key
Since the entry is not there, a new one gets inserted into the map
Since the key of the entry is known, but the value is not, a default (value-initialized, e.i. 0 for int) object for the value gets created
A reference to the newly created object (in this case, int& initialized to zero) is returned to the caller
The ++ operator is applied to the reference returned from the [], which changes 0 to 1 (or 0.0 to 1.0, etc.)
Yes. When you use the []-operator on a map and no element with the desired key exists, a new element is inserted which is value-initialized. For an integer, this means initialized to zero.
does it work for all cases?
For all cases, a new key will be associated with a value initialized to T().
When T is a built-in or Plain Old Data type, such as int or double, that evaluates to zero.
When T is a class, the map will attempt to call the empty constructor.

C++: How can I stop map's operator[] from inserting bogus values?

My code did the following:
Retrieve a value from a map with operator[].
Check the return value and if NULL use insert to insert a new element in the map.
Magically, an element with value 0 appeared in the map.
After several hours of debugging I discovered the following: map's operator[] inserts a new element if the key is not found while insert does not change the value if the key exists.
Even if a default constructor for the map value type does not exist the code compiles and operator[] inserts 0.
Is there a way (e.g. some coding convention I could follow from now on) I could have prevented this from hurting me?
Is there a way (e.g. some coding convention I could follow from now on) I could have prevented this from hurting me?
This may sound snarky, but: by reading the documentation.
Since what you did is somewhat expected behaviour of the map, there’s not much you can do to guard against it.
One thing you can heed in the future is the following. In your second step, you did something wrong:
Check the return value and if NULL use insert to insert a new element in the map.
This does never work with C++ standard library functions (other than C compatibility functions and new): the standard library doesn’t deal in pointers, least of all null pointers, so checking against NULL (or 0 or nullptr) rarely makes sense. (Apart from that, it wouldn’t make sense for a map’s operator [] to return a pointer in the first place. It obvoiusly returns the element type (or rather, a reference to it)).
In fact, the standard library predominantly uses iterators so if at all, check for iterator validity by comparing against the end() of a container.
Unfortunately, your code (checking against NULL) compiled since NULL is actually a macro that’s equal to 0 in C++ so you can compare it against an integer.
C++11 gets safer by introducing the nullptr keyword which has a distinct type, so comparing it with an integer wouldn’t compile. So this is a useful coding convention: never use NULL, and instead compile with C++11 support enabled and use nullptr.
I guess the obvious is to learn that those are indeed the semantics of the indexing operator, so you should not use it to test for element existance in a container.
Instead, use find().
Indeed, when you call operator [], if a value at that key is not found, a value-initialized default value is inserted.
If you don't want this to happen, you have to check with find:
if ( mymap.find(myKey) == mymap.end() )
{
//the key doesn't exist in a map
}
The value returned by operator [] will be NULL only if it's a map to pointers (or types that value-initialized yield 0, but you were pretty specific about NULL).
Even if a default constructor for the map value type does not exist
the code compiles
This is definitely wrong. operator[] should fail to compile if a default constructor does not exist. Anything else is an error on the part of your implementation.
Your code should have simply used insert once.
Latest implementations of std::map also have a .at(const Key& key) member functions which checks for existence of value and returns a std::out_of_range exception if the key has not been found.
http://en.cppreference.com/w/cpp/container/map/at
Instead of using the [] operator, call find on your map. This will not insert an entry if no match is found. It returns an iterator to the item found.
Here are several examples of map lookup and insert use cases, where you sometimes want to handle the case of items already existing, seeing what old value was, etc.
class Foo
{
// Use a typedef so we can conveniently declare iterators
// and conveniently construct insert pairs
typedef map<int, std::string> IdMap;
IdMap id_map;
// A function that looks up a value without adding anything
void one(int id)
{
IdMap::iterator i = id_map.find(id);
// See if an entry already exists
if (i == id_map.end())
return; // value does not exist
// Pass the string value that was stored in the map to baz
baz(i->second);
}
// A function that updates an existing value, but only if it already exists
bool two(int id, const std::string &data)
{
IdMap::iterator i = id_map.find(id);
if (i == id_map.end())
return false;
i->second = data;
return true;
}
// A function that inserts a value only if it does NOT already exist
// Returns true if the insertion happened, returns false if no effect
bool three(int id, const std::string &data)
{
return id_map.insert(IdMap::value_type(id, data)).second;
}
// A function that tries to insert if key doesn't already exist,
// but if it does already exist, needs to get the current value
void four(int id, const std::string &data)
{
std::pair<IdMap::iterator,bool> i =
id_map.insert(IdMap::value_type(id, data));
// Insertion worked, don't need to process old value
if (i->second)
return true;
// Pass the id to some imaginary function that needs
// to know id and wants to see the old string and new string
report_conflict(id, i->first->second, data);
}
};
Programmers often do multiple redundant calls to operator[], or a call to find then a redundant call to operator[], or a call to find then a redundant call to insert, out of laziness or ignorance. It is quite easy to efficiently use a map if you understand its semantics.
operator[] actually returns a Value& so if you are sure you want to insert the element you can do something like:
map<Key, Value*> my_map;
Value& entry = my_map[key]; // insert occurs here, using default constructor.
if (entry == nullptr) entry = my_new_entry; // just changing the value
Added benefit is that you only lookup in the map once.