C++ Structured bindings question regarding unordered_map - c++

I am learning new C++17 features and I came across this:
std::vector<int> nums = { 1, 1, 2, 3 };
std::unordered_map<int, size_t> m;
for (int i = 0; i < nums.size(); ++i)
{
const auto& [inserted_entry, inserted_happen] = m.emplace(nums[i], i);
std::cout << inserted_happen << "\n";
}
The result is:
1
0
1
1
What is happening here? I do not understand.
Also what is inserted_entry?

emplace returns a pair of an iterator to inserted element(or already present element) and a bool representing if the insert was successful.
inserted_happen is a bool.
The second insert fails since 1 already exists as key in the map.

emplace returns std::pair<iterator, bool> which then gets "destructured" and 2 bindings get created. inserted_entry is a reference to the iterator part and inserted_happen is a reference to the bool part.

Return value of emplace is a pair.
The first element of the pair is the iterator for the newly inserted element (if insertion was successful) or of an existing element (if insertion failed).
The second part of the returned pair is a bool value (in your case inserted_happen) indicating whether insertion succeeded.
In your case, the second emplace failed as you have already inserted an element with key equal to 1.
If you want to change the value part of the element, you can just try -
m[nums[i]] = i;

Related

Inserting an element in given positions (more than one) of a vector

I am trying to add a certain value in loop to a given position of a certain vector. For example:
the value is 11 and hence local_index = 11
The vector of position I have in input is neigh = {5,7}
The starting vector is Col={0,1,2,3,4,5,6,7,8,9,10}
I want to have as output Col={0,1,2,3,4,5,11,6,7,11,8,9,10}. This is my first try:
vector<long> neigh = {5,7};
long local_index = 11;
auto pos_col = Col.begin();
for (const auto& elem: neigh) {
Col.insert(pos_col+elem,local_index);
I keep getting for the second value of neigh a segmentation fault. So my questions are:
Is this because the insert return a pointer that cannot be re-assigned?
If the answer to the first question is yes, how can I achieve my goal?
Per the vector::insert() documentation on cppreference.com:
Causes reallocation if the new size() is greater than the old capacity(). If the new size() is greater than capacity(), all iterators and references are invalidated. Otherwise, only the iterators and references before the insertion point remain valid. The past-the-end iterator is also invalidated.
Which means that, after your 1st call to insert(), your pos_col iterator is now invalid for subsequent calls to insert(), as it refers to an element before the insertion point.
Try using this instead:
auto pos_col = Col.begin();
for (const auto& elem: neigh) {
Col.insert(pos_col+elem,local_index);
pos_col = Col.begin();
}
Or simply:
Col.insert(Col.begin()+elem,local_index);

What does "result.second == false" mean in this code?

I came across this c++ code for counting frequency in a vector.
std::map<std::string, int> countMap;
// Iterate over the vector and store the frequency of each element in map
for (auto & elem : vecOfStrings)
{
auto result = countMap.insert(std::pair<std::string, int>(elem, 1));
if (result.second == false)
result.first->second++;
}
from https://thispointer.com/c-how-to-find-duplicates-in-a-vector/. I want to ask what does
result.second == false mean?
Since std::map and the other non-multi associative containers only store unique items there is a chance that when you insert something into it it wont actually insert since it may already be present. insert therefore returns a std::pair<iterator, bool> where the bool will be true if the insert succeeded and false otherwise.
I would like to point out you can get rid of the if statement in the loop. Because of how operator[] of a map works the loop can be replaced with
for (const auto & elem : vecOfStrings) // also added const here since we don't need to modify elem
{
++countMap[elem];
}
And now if elem exists then you increment the value and if it doesn't you added elem to the map and increment its value.
std::map::insert returns a std::pair<iterator, bool>.
pair.first is an iterator to the newly inserted element OR the element that was already in the map and prevented the insertion.
pair.second tells whether or not the insertion happened.
result.second == false is detecting the case where nothing was inserted into the map due to a key collision.
Note that with C++17, this can be written to be a bit more clear:
auto [itr, inserted] = countMap.insert({elem, 1});
if (!inserted) {
itr->second++;
}
From cppreference:
Returns a pair consisting of an iterator to the inserted element (or
to the element that prevented the insertion) and a bool denoting
whether the insertion took place.
result.first gives you the iterator to the element, while result.second tells you whether the element was actually inserted or did already exist.
std::map::insert returns a pair where the second value indicates whether any insertion actually happened. If the value is false, this means no value was inserted into the map because a value with the same key already exists.
However, the code shouldn’t be written like this: comparing against boolean literals is a nonsensical operation. Instead you’d write
if (not result.second)
// or
if (! result.second)
std::map::insert returns a pair of iterator and a bool. The bool indicates whether the insertion actually took place. The code you listed seems to increment the mapped int if key collision happens on insert.

What happens when a duplicate value is inserted in a Map in C++

void main()
{
std::map<int,int>keyValueMap;
keyValueMap.insert(0,1);
keyValueMap.insert(0,2);
int index = keyValueMap.begin()->second;
}
the value of index at thispoint is 1 and not 2 can someone explain why ?
From this std::map::insert reference:
Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.
[Emphasis by me]
You can't use insert to overwrite existing data in the map.
All of the 2-param overloads of std::map::insert() require iterators, not key/value pairs.
Most standard container insert() methods take the container's value_type as input. The value_type of a std::map is a std::pair. So, when inserting a key/value pair into a std::map, you have to pass them in to insert() as a std::pair, eg:
void main()
{
std::map<int,int> keyValueMap;
keyValueMap.insert(std::make_pair(0, 1));
keyValueMap.insert(std::make_pair(0, 2));
int index = keyValueMap.begin()->second;
}
Now, that being said, the second insert() will still not overwrite the value from the first insert(), even though they both have the same key 0. The second insert will simply be ignored:
Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.
...
Return value
1-3) Returns a pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool denoting whether the insertion took place.
To overwrite the first value with the second value, you can either:
use the iterator that insert() returns:
void main()
{
std::map<int,int> keyValueMap;
keyValueMap.insert(std::make_pair(0, 1));
auto ret = keyValueMap.insert(std::make_pair(0, 2));
if (!ret.second) ret.first->second = 2;
int index = keyValueMap.begin()->second;
}
use the map's operator[] instead:
void main()
{
std::map<int,int> keyValueMap;
keyValueMap[0] = 1;
keyValueMap[0] = 2;
int index = keyValueMap.begin()->second;
}

C++ list iterator to integer conversion?

Hello guys I have a list iterator type which I want to convert to UINT.
std::set<UINT> volSet;
UINT ssidBase = 1;
UINT ssidEnd = 2;
volSet.insert( ssidBase );
volSet.insert (ssidEnd);
UINT ssidStart = evf::volSet.begin();
I want the first value in the volSet to be set to ssidStart. I'm getting an error when I run this?
cannot convert 'std::_Tree<std::_Tset_traits<unsigned int, std::less< unsigned int>, std::allocator< unsigned int>, false> >::iterator' to 'UINT' in initialization
Any help is appreciated?
As such, iterators in C++ are basically pointers.
So the general idea is that you need to dereference that iterator to access the actual value it points to, like so:
UINT number = *(container.begin());
It is not clear exactly what the intention is with inserting a one and then a two. Granted many more unsigned values can be inserted later. However, by inserting a one, the only other possible value that will be lower is zero. The insert method for the STL set, map, unordered_set, and unordered_map containers (now called Standard Library) returns a pair.
Insert method reference links:
set
http://www.cplusplus.com/reference/set/set/insert/
map
http://www.cplusplus.com/reference/map/map/insert/
unordered_set
http://www.cplusplus.com/reference/unordered_set/unordered_set/insert/
unordered_map
http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/
If the element already exists in a set, the first pair element is the iterator pointing to the existing element with the second element set to false. With a successful insert (e.g. the key is not already present in the set), the first element of the pair points to the element that has been successfully added, with the second pair element set to true.
Using a set to guarantee the first (lowest) element (e.g. where the begin() method will always return an iterator pointing to unless the set is empty), does guarantee that the lowest value in the set is always found in constant time (e.g. O(1); big Oh of one).
A coding example with some values in C++11.
std::set<UINT> volSet;
pair <std::set<UINT>::itertor, bool> p;
//
// Using the same notation on unsigned int for consistency,
// insert 500 unsigned values into the volSet container.
//
for (UINT i = 500; i >= 0; i--)
{
p = volSet.insert(i);
if (p.second == false)
{
cerr << "volSet insertion warning!! The value for volSet already has: ";
cerr << i << endl;
}
}
//
// Do business logic, deletions/insertions from/to the volSet, and so on...
//
// Now obtain the lowest value inside the volSet and set it to a local variable.
// It is assumed that evf is a user defined namespace.
//
std::set<UINT>::iterator ii = evf::volSet.begin();
UINT ssidStart = *ii;
//
// Alternatively, implement without declaring a local iterator as Victor
// has shown above. Note that the parenthesis here are important. The call
// to begin() has to occur prior to the dereference operator call.
// Note that the sample above intentionally sets the ii iterator
// before dereferencing.
//
UNIT lowValue = *(evf::volSet.begin());
I hope this is helpful to understand the difference between iterators and container elements.

Issues in searching through the vector?

I have a vector of the form given below (in C++):
vector<pair<int,int> > u;
Now when the first element of u.first becomes equal to 12 then I want to break from the loop. I am using the following code for this:
while(1){
if((find(u.begin().first, u.end().first, 12)!=u.end().first))
{
break;
}
}
However, it gives me the error that
'unable to resolve identifier first'
std::find iterates over a range and returns an iterator to the first element in the sequence that matches the provided value (12, in your case). The iterators are not the element in the container, they are pseudo-references to elements in the container.
You have to dereference an iterator to get the element. So, u.begin()->first would be the first value of the initial element of the container. u.begin().first is nonsensical.
In any case, to find a matching element using an operation other than ==, you need to use find_if with a custom predicate. For example, using a lambda expression:
auto const it(std::find_if(u.begin(), u.end(), [](std::pair<int, int> const& v)
{
return v.first == 12;
}));
if (it != u.end())
continue;