stl : map of vectors - begin() address - c++

I've been fighting for several hours to understand why begin() of vector within a map doesn't return the same address if I point to the vector.begin() itself or the second.begin() member of the map.
Let me explain:
I have a class containing a map which key is an int and its content a vector of int. I have to go through the map and keep in memory the position where I was 'just before', so I also have another map of iterators (of the first map).
So, I would like to get something like:
map1 : <2,<3,8,1,3,7,1>>
map2 : <8,<6,9,1,3>>
map3 : <1,<3,1>>
etc.
To make it simpler, in my code, the T_IPC_CommandId is just an enum of int.
I have a method called Add(int TopCommand, int Command) which fills/creates the map of vectors of commands. For example, to create map3, I will code:
Add(1,3), Add(1,1)
I have a method called GetNext(int Command) which returns the next int, in the map pointed by Command. For example in map2, calling Get(8) will return 6, and the next call will return 9, then 1, 3 and 0 for the next calls.
In order to know which int I will return, I need to keep track, for each map, what is the next int to return. So, I use a IPC_CommandId_Pointer which is a map which key is the command itself and holds the iterator where I am in the map. And here is the problem:
At each Add() call, I initialise this IPC_CommandId_Pointer to the beginning of main map. Like this:
void T_ListOfCommand::Add(T_IPC_CommandId Top_CommandId, T_IPC_CommandId IPC_CommandId)
{
T_IPC_CommandId_Vec_Iter Vec_Iter;
T_IPC_CommandId_Vec Vec;
if ((IPC_CommandId_Map.find(Top_CommandId)) == IPC_CommandId_Map.end())
{
IPC_CommandId_Map[Top_CommandId].clear();
}
IPC_CommandId_Map[Top_CommandId].push_back(IPC_CommandId);
// Repeated at each add, but don't care ...
Vec_Iter = IPC_CommandId_Map[Top_CommandId].begin();
IPC_CommandId_Pointer[Top_CommandId] = Vec_Iter;
}
The problem I have is that, at each IPC_CommandId_Map[Top_CommandId].push_back(IPC_CommandId), the IPC_CommandId_Map[Top_CommandId].begin() doesn't return the same address.
Now, when replacing the:
Vec_Iter = IPC_CommandId_Map[Top_CommandId].begin();
IPC_CommandId_Pointer[Top_CommandId] = Vec_Iter;
With:
Vec = IPC_CommandId_Map[Top_CommandId];
IPC_CommandId_Pointer[Top_CommandId] = Vec.begin();
This works fine.
I'm supposing to point to the same location when using the begin() in the map, or when dereferencing the vector, isn't it? Well, it seems not.
If someone could explain me the difference, that would be great.

vector::begin() is not guaranteed to return the same address each time!
If std::vector::push_back() causes reallocation of the vectors data (since its data has to be continuous), the iterator (address) which begin() returns will be different.
And (as The Paramagnetic Croissant stated in the comment) the code Vec = IPC_CommandId_Map[Top_CommandId]; copies the element of the map to the variable Vec.

(Vocabulary check: you're not dereferencing anything, particularly not a vector.)
Vec_Iter = IPC_CommandId_Map[Top_CommandId].begin();
is an iterator into the vector in the map.
It becomes invalid when the vector is reallocated.
Every time the vector is reallocated you will get a different value for begin().
It's a bad idea to store this iterator across calls to push_back or anything else that may invalidate an iterator.
Vec = IPC_CommandId_Map[Top_CommandId];
IPC_CommandId_Pointer[Top_CommandId] = Vec.begin();
is an iterator into the local copy Vec.
It becomes invalid as soon as the function returns.
It's a bad idea to store this iterator anywhere beyond Vec's lifetime.
Overall, iterators should be considered transient and only be used in as small a scope as possible.
A much more robust solution is to store a "current index" for the vector instead of an iterator.

Related

Can we take the value of iterator which was returned from lower_bound as vector index?

I'm new to vector in C++ and trying to get how it works.
First, I have a vector array:
vector<int>container;
Then I want to get the position of a given number in a vector array.
vector<int>::iterator position;
position = lower_bound(container.begin(), container.end(), temp);
After that, I want to get the value at that position that was returned from lower_bound by
container[position]
But I get the error that
No viable overloaded operator[] for type 'vector'
When I change it into *(position+1), it works fine.
So what is the different between those two?
Welcome to stackoverflow :)
First of all, we should understand what an iterator is. According to the hackingcpp
objects that point to a location
may point to a readable memory address / object
..
There are a lot of containers in C++ STL, such as vector, list, map and others.
A iterator is an abstraction of pointer, which allows you to access elements stored in container, use algorithm(such as sort, find, lower_bound) provided by STL no matter what kind of container we have.
Therefore, the return type of std::lower_bound is an iterator as you know vector<int>::iterator,
You couldn't access an element by calling container[position], there is no such function vector[iterator] provided by vector.
When I change it into *(position+1)
*itreator means return the value of where an iterator points out.
By the way, it's dangerous to do something like this *(position+1).
Since perhaps the given value tmp is in vector, perhaps it's not, so you should check whether the given value tmp is in vector by calling iterator != vector.end().
std::lower_bound returns a ForwardIterator (See: C++ named requirements: LegacyForwardIterator).
You can dereference it just like you would a pointer, eg:
std::vector<int> container;
// ...
auto it = std::lower_bound(container.begin(), container.end(), foo);
if (container.end() == it) {
throw or_something("handle error?");
}
const int x = *it;
const int y = container[std::distance(container.begin(), it)];
In that example x == y is true. (See: Compiler Explorer)

Can I get a container object from an iterator?

std::vector<int> vec={1,2,3};
std::vector<int>::iterator it = vec.begin();
if(vec == get_vec_from_it(it)){
puts('sucesss');
}
std::vector<int> get_vec_from_it(std::vector<int>::iterator it){
/*?*/
}
How should I write get_vec_from_it function in the above example?
The basic idea is that iterators abstract away where the elements come from, there might not even be a container. Afaik there is a single type of iterator that "knows" its container and that is std::back_insert_iterator, though thats an exception. The container member is only protected so there is even a way to get the container from a std::back_insert_iterator, but thats not how it is meant to be used.
You can adance the iterator to get the next element, but you wouldn't know where to stop, because at some point you'll reach the end of the vector and there is no way to identify it. If you pass begin and end you can create a copy of the original vector:
std::vector<int> get_vec_from_it(std::vector<int>::iterator begin ,std::vector<int>::iterator end){
return {begin,end};
}
Though, thats just a different way to copy the vector and you need to know both begin and end.
I made a function that returns the iterator that points to the node but I couldn't write the stop condition in a for statement. So I wonder if the stop condition can be written like it!=get_vec_from_it(it).end()
Functions that work on a range of elements typically take a pair of iterators, first and last, to know where to stop (alternatively a first iterator and number of elements can be used). Your idea of using it!=get_vec_from_it(it).end() is overcomplicating the issue. Just pass vec.end() to the function and use that: it != end.
No.
You can create a vector from a pair of iterators, or an iterator and number of elements. Example:
std::vector<int>
get_vec_from_its(std::vector<int>::iterator first, std::vector<int>::iterator last){
return std::vector<int>(first, last);
}
// ...
if(vec == get_vec_from_it(vec.begin(), vec.end())){
The function is of course so trivial that I would recommend instead to use the constructor directly.

Is it at all possible to erase from a vector with C++11's for loops?

Alright. For the sake of other (more simple but not explanatory enough) questions that this might look like, I am not asking if this is possible or impossible (because I found that out already), I am asking if there is a lighter alternative to my question.
What I have is what would be considered a main class, and in that main class, there is a variable that references to a 'World Map' class. In essence, this 'WorldMap' class is a container of other class variables. The main class does all of the looping and updates all of the respective objects that are active. There are times in this loop that I need to delete an object of a vector that is deep inside a recursive set of containers (As shown in the code provided). It would be extremely tedious to repeatedly have to reference the necessary variable as a pointer to another pointer (and so on) to point to the specific object I need, and later erase it (this was the concept I used before switching to C++11) so instead I have a range for loop (also shown in the code). My example code shows the idea that I have in place, where I want to cut down on the tedium as well as make the code a lot more readable.
This is the example code:
struct item{
int stat;
};
struct character{
int otherStat;
std::vector<item> myItems;
};
struct charContainer{
std::map<int, character> myChars;
};
int main(){
//...
charContainer box;
//I want to do something closer to this
for(item targItem: box.myChars[iter].myItems){
//Then I only have to use targItem as the reference
if(targItem.isFinished)
box.myChars[iter].myItems.erase(targItem);
}
//Instead of doing this
for(int a=0;a<box.myChars[iter].myItems.size();a++){
//Then I have to repeatedly use box.myChars[iter].myItems[a]
if(box.myChars[iter].myItems[a].isFinished)
box.myChars[iter].myItems.erase(box.myChars[iter].myItems[a]);
}
}
TLDR: I want to remove the tedium of repeatedly calling the full reference by using the new range for loops shown in C++11.
EDIT: I am not trying to delete the elements all at once. I am asking how I would delete them in the matter of the first loop. I am deleting them when I am done with them externally (via an if statement). How would I delete specific elements, NOT all of them?
If you simply want to clear an std::vector, there is a very simple method you can use:
std::vector<item> v;
// Fill v with elements...
v.clear(); // Removes all elements from v.
In addition to this, I'd like to point out that [1] to erase an element in a vector requires the usage of iterators, and [2] even if your approach was allowed, erasing elements from a vector inside a for loop is a bad idea if you are not careful. Suppose your vector has 5 elements:
std::vector<int> v = { 1, 2, 3, 4, 5 };
Then your loop would have the following effect:
First iteration: a == 0, size() == 5. We remove the first element, then the vector will contain {2, 3, 4, 5}
Second iteration: a == 1, size() == 4. We then remove the second element, then the vector will contain {2,4,5}
Third iteration: a == 2, size() == 3. We remove the third element, and we are left with the final result {2,4}.
Since this does not actually empty the vector, I suppose it is not what you were looking for.
If instead you have some particular condition that you want to apply to remove the elements, it is very easily applied in C++11 in the following way:
std::vector<MyType> v = { /* initialize vector */ };
// The following is a lambda, which is a function you can store in a variable.
// Here we use it to represent the condition that should be used to remove
// elements from the vector v.
auto isToRemove = [](const MyType & value){
return /* true if to remove, false if not */
};
// A vector can remove multiple elements at the same time using its method erase().
// Erase will remove all elements within a specified range. We use this method
// together with another method provided by the standard library: remove_if.
// What it does is it deletes all elements for which a particular predicate
// returns true within a range, and leaves the empty spaces at the end.
v.erase( std::remove_if( std::begin(v), std::end(v), isToRemove ), std::end(v) );
// Done!
I am deleting them when I am done with them externally (via an if statement). How would I delete specific elements, NOT all of them?
In my opinion, you're looking at this the wrong way. Writing loops to delete items from a sequence container is always problematic and not recommended. Strive to stay away from removing items in this fashion.
When you work with containers, you should strategically set up your code so that you place the deleted or "about to be deleted" items in a part of the container that is easily accessed, away from the items in the container that you do not want to delete. At the time you actually do want to remove them, you know where they are and thus can call some function to expel them from the container.
One answer was already given, and that is to use the erase-remove(if) idiom. When you call remove or remove_if, the items that are "bad" are moved to the end of the container. The return value for remove(_if) is the iterator to the start of the items that will be removed. Then you feed this iterator to the vector::erase method to delete these items permanently from the container.
The other solution (but probably less used) is the std::partition algorithm. The std::partition also can move the "bad" items to the end of the container, but unlike remove(_if), the items are still valid (i.e. you can leave them at the end of the container and still use them safely). Then later on, you can remove them as you wish in a separate step since std::partition also returns an iterator.
Why not have a standard iterator iterating over a vector. That way you can delete the element by passing an iterator. Then .erase() will return the next available iterator. And if your next iterator is iterator::end() then your loop will exit.

c++: popping an element by key out of an std::map

I'm interested in removing an element with a specific key out of a map and use this element.
Something that will look like:
itr = MyMap.pop(wantedKey);
//Now MyMap is missing the element which has the key 'wantedKey'.
//Do something with this element through 'itr'.
Is there an stl map method for doing this?
EDIT
Following carleeto's response, I want to clarify: What I need is the element being removed from the map and the program being able to use it afterwards, it could be the element itself as a pair, not necessarily an iterator.
There are two options: use it in-place then remove it, or move it to a local variable, remove the entry, then use it.
// use-remove
auto i = MyMap.find(wantedKey);
if (i != MyMap.end()) {
// use-remove
use(i->second);
MyMap.erase(i);
// or
// move-remove-use
auto x = std::move(i->second);
MyMap.erase(i);
use(x);
} else {
// Not found
}
Not that I know of, but you can use std::map::find to get an iterator and then call std::map::erase with said iterator as an argument when you're done.
From your variable naming, I think you might be confusing concepts here.
itr = MyMap.pop(wantedKey);
//Do something with this element through 'itr'.
Iterators only point to elements in containers. Therefore, if you had received an iterator through a function called pop (even if it existed), the iterator would reference not the element you popped, but probably the one after or before it, like std::vector::erase. This is because the purpose of an iterator is to iterate over the elements in a container. Therefore, if an element is not in the container, you cannot get an iterator to it. However, even if you used the iterator returned by the erase function, it would not reference you would be expecting it to.
So you can erase an element from the map, like so many have pointed out, by searching for it, getting the ierator to it and then calling erase with that iterator. but you cannot get an iterator that points to element you have erased. Hope this clears things up.
UPDATE: If all you want is to access the element and use it, then all you need to do use std::map::find to get an iterator and std::map::erase to remove the item from the map, once you have finished using the iterator. The reason is that even if you have stored a copy of the iterator for future use, once you call erase, it will be invalidated. To be able to access it after you have erased it, depending on scope, you will probably need to copy it.
Finally, what you want to do is a very common task - look up a map based on a key and perform an operation on the associated element. It's quite likely that you have a list of keys to go through. You should also look up functors, std::for_each and std::transform. I realise this is not operating on the element after you have removed it, but I thought I would add it in, seeing as how its a related operation. For example: You could move all elements that match a list of keys into another container (say, a vector, and then use the above to operate on them).
Probably what you want to do is
itr = MyMap.find('thing in a string');
to find the iterator and then use it,
MyMap.erase(itr)
And then erase it.
Pop() belongs to the stack datastructure. To access an element of a map, use the [] operator (http://www.cplusplus.com/reference/map/map/operator%5B%5D/), to remove it from the map use (http://www.cplusplus.com/reference/map/map/erase/).
itr = MyMap.find(wantedKey);
if(itr != MyMap.end()) {
use( itr->second );
MyMap.erase(itr);
}
your.uncle = bob;
Using C++'s std::map<T, U>::find():
map.erase(map.find(key));
The way I did it is below. In my case the map stores std::shared_ptr values, making the copy cheap(ish), and the object ownership transferral clear.
auto it = MyMap.find( wantedkey );
if ( it == MyMap.end() ) throw runtime_error("not found");
auto ret = it->second; // make copy of shared_ptr
MyMap.erase(it);
return ret;
The caller gets a shared_ptr with a reference count of at least one (from the copy). Note the function must return the shared_ptr by value, etc.

STL Vector, Iterator and Insert (C++)

I have a method to which a vector's iterator is passed.
In this method I'd like to add some elements into the vector, but I am not sure whether this is possible when having only the iterator
void GUIComponentText::AddAttributes(vector<GUIComponentAttribute*>::iterator begin, vector<GUIComponentAttribute*>::iterator end)
{
for (vector<GUIComponentAttribute*>::iterator i = begin; i != end; ++i)
{
GUIComponentAttribute &attrib = *(*i);
// Here are the GUIComponentAttribute objects analyzed - if an object of a
// special kind appears, I would like to add some elements to the vector
}
}
Thanks
Markus
In the code you show, this is not possible. Especially because you should not add/remove elements to/from a vector while you iterate over it.
This is a long standing design "issue" in the STL. Iterators do not allow the modification of the structure of the underlying sequence they are iterating over: ie you can modify (sometimes) the elements themselves, but you cannot add/remove elements. Though InputIterator and OutputIterator are a bit special in this regard... hum...
This is actually the cause of the erase/remove idiom:
vec.erase(std::remove_if(vec.begin(), vec.end(), predicate), vec.end());
So, no, sorry, there is no way to actually modify the vector.
However, as exposed above, you can perfectly use the remove_if algorithm and simply return the new end of the valid range... or you can ask for the whole vector to begin with.
As noted by Björn, modifying a sequence structure while iterating over it is error-prone.
First, you'll have to change the interface. Given two iterators,
there's no way to get back to the container to which they refer; so if
you want to modify the container, you'll have to pass a reference to it,
i.e.:
void GUIComponentText::AddAttributes(
std::vector<GUIComponentAttribute*>& attributes )
{
for ( std::vector<GUIComponentAttribute*>::iter = attributes.begin();
iter != attributes.end();
++ iter )
{
// ...
}
}
Having done that: insertion can invalidate iterators. So it depends on
where you want to insert. If you want to insert at the current
position: std::vector<>::insert of a single element returns an
iterator to that element, which was inserted before your element, so you
can assign it to your iterator, adjust (if necessary), and continue:
iter = attributes.insert(iter, newAttribute);
++ iter; // Return to where we were...
If you're appending (push_back), the problem is a bit more complex;
you need to calculate the offset, then reconstruct the iterator:
size_t offset = iter - attributes.begin();
attributes.push_back( nweAttribute );
iter = attributes.begin() + offset;
In this case, it is probably simpler to iterate using a size_t and
[], rather than an iterator.
It is not possible to add elements into a vector whilst iterating over it. In addition, you most certainly cannot add one to a vector with just a pair of iterators- you'd need a pointer/reference to the whole vector object.
The best you could do is return a vector of new components to add by the the calling function.