I'm lost: An iterator of a vector of std::string works perfectly unless there is a function call (Z_UB->set() ) before it++. Here's the code:
std::vector< std::string >::iterator it = g_SPP.scenarios->getVector().begin();
std::cout << "begin of vector: " << *it << std::endl;
Z_UB->set("s1", "scn2", 350);
it++;
std::cout << "second of vector: " << *it << std::endl;
creates the following output
begin of vector: scn1
However, if I move the function call like this:
std::vector< std::string >::iterator it = g_SPP.scenarios->getVector().begin();
std::cout << "begin of vector: " << *it << std::endl;
it++;
std::cout << "second of vector: " << *it << std::endl;
Z_UB->set("s1", "scn2", 350);
The result is the following, which is the expected behaviour:
begin of vector: scn1
second of vector: scn2
Inside the Z_UB->set() function there is nothing left but the call itself:
void Parameter::set( std::string _i, std::string _j, float value) {
//int i = indexSets[0]->backIndex(_i);
//int j = indexSets[1]->backIndex(_j);
//data2D[0][0] = value;
}
So if I call the Z_UB->set() function after I created the iterator, accessing it will crash the program. Is there anything vital that I missed about Iterators?
Several possibilities:
Either you do not have a good reproductible example: maybe in your first run you had only one element in your vector (how is it filled?), and invoked undefined behaviour because you did not check it against g_SPP.scenarios->getVector().end()
Or Z_UB->set does not do what you think. Is it a polymorphic class? Is set virtual? Is the -> operator overloaded?
Is your app multithreaded and another thread is mutating your container?
If g_SPP is a global variable then iterators over it will be invalidated by any mutating operation.
Update -- this is from the 1998 ISO/ANSI spec:
The following invalidate all references, iterators, and pointers referring to elements of the sequence if an allocation is required. An allocation is required if the current capacity() is less than the target vector size.
void reserve(size_type n)
iterator insert(iterator position, const T& x),
void insert(iterator position, size_type n, const T& x),
void insert(iterator position, InputIterator first, InputIterator last), and
Erasure invalidates all references, iterators, and pointers referring to elements after the position of the initial erased element.
iterator erase(iterator position)
iterator erase(iterator first, iterator second)
Resizing a vector is equivalent to calling either insert or erase. According to 23.2.4.2/6: resize(sz, c=value_type()) has the same effect as:
if (sz > size())
insert(end(), sz - size(), c);
else if (sz < size())
erase(begin() + sz, end());
else
;
a std::vector<T>::iterator will be invalidated if you add or remove elements while iterating over it, if the vector needs to resize itself internally, when an element is added.
.getVector() returned a copy of the vector. Comparing an iterator to the end point of the iterator of a completely different object doesn't make sense. Returning a reference solved the problem.
#Xeo also pointed out a much better explanation: When creating an iterator from a copy like this:
std::vector< std::string >::iterator it = g_SPP.scenarios->getVector().begin();
the copy immediately is destroyed thus invalidating the just created iterator. So the iterator shouldn't have returned the first element in the first place, but I can merely guess that this is hidden deeply in the implementation of the compiler.
Related
I want to observe the difference between cbegin and begin.
But when i use cbegin i am getting the same result as begin.
According to definition cbegin will return const itertaor and we cant modify the element using the const iterator returned by cbegin.
But, still i am able to erase the element at particular position.
for (auto i = g1.cbegin(); i != g1.cend(); ++i){
cout << *i << " ";
}
//below code erases element at const pointer
g1.erase(i);
The member function erase accepts const_iterator(s).
For example
iterator erase(const_iterator position);
In early Standards the function indeed was declared with non-constant iterators.
Take into account that the function returns a non-constant iterator but it can be converted implicitly to a constant iterator and can be compared with constant iterators.
By the way this call
g1.erase(i);
erases nothing because after the loop i is equal to the iterator returned by the function cend provided that the name i is defined before the loop.
auto i = g1.cbegin();
for (; i != g1.cend(); ++i){
cout << *i << " ";
}
//below code erases element at const pointer
g1.erase(i);
You can erase an element of the vector using the const_iterator because the vector itself is not constant. If the vector would be constant you could not erase its element.
That is the erase member function changes the vector itself (so it may not be applied to a constant vector), but it does not change elements of the vector using the const_iterator.
Thanks a lot Vlad from Moscow.
I just tried *i=3; in the loop where i am using cend and cbegin.
for (auto it = g1.cbegin(); it != g1.cend(); ++it){
cout << *it << " ";
*it=3;
}
I got compilation error :
error: assignment of read-only location ‘it.__gnu_cxx::__normal_iterator >::operator*()’
*it=3;
^
I am finding trouble inserting an object into a std::vector, following the example of what I'm trying to do:
//in SomeClass.cpp
void SomeClass::addItem(int32_t &position, OtherClass &value)
{
vectorOtherClass.insert(position,valvalue);
}
however I get the following error when trying to compile the program:
error: no matching function for call to ‘std::vector::insert(int32_t&, OtherClass&)’
vectorOtherClass.insert(position,value);
______________________^
the vector definition in SomeClass.h is:
private:
std::vector<OtherClass> vectorOtherClass;
How can I properly insert an object into a vector in C ++?
And one last question, are the objects stored by reference or by copy within the vector?
Like the error says, there is no insert function with int parameter. See:
single element (1)
iterator insert (iterator position, const value_type& val);
fill (2)
void insert (iterator position, size_type n, const value_type& val);
range (3)
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
You can find an example here http://www.cplusplus.com/reference/vector/vector/insert/
int main ()
{
std::vector<int> myvector (3,100);
std::vector<int>::iterator it;
it = myvector.begin();
it = myvector.insert ( it , 200 );
myvector.insert (it,2,300);
// "it" no longer valid, get a new one:
it = myvector.begin();
std::vector<int> anothervector (2,400);
myvector.insert (it+2,anothervector.begin(),anothervector.end());
int myarray [] = { 501,502,503 };
myvector.insert (myvector.begin(), myarray, myarray+3);
std::cout << "myvector contains:";
for (it=myvector.begin(); it<myvector.end(); it++)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
According to the method's reference, the insert method takes the following parameters:
position
Position in the vector where the new elements are inserted.
iterator is a member type, defined as a random access iterator type that points to elements.
val
Value to be copied (or moved) to the inserted elements.
Member type value_type is the type of the elements in the container, defined in deque as an alias of its first template parameter (T).
Note that position is not an integral value, but rather an iterator. C++ use iterators a lot, since many operations are fairly efficient when you have one.
In particular, you can add vector iterators and numbers to get the iterator to the correct position, so you can do something like:
vectorOtherClass.insert(vectorOtherClass.begin() + position, value);
This does not apply to other containers such as std::list. Also, you should make sure to check position is within the bounds of the vector (0 <= position < vectorOtherClass.size()). Ideally, position should be unsigned to ensure the lower bound.
Lastly, elements are added to std::vectors as copies. Vectors use an array internally, so values are copied into it. The array is resized (copied and replaced) as needed.
Refer to: http://en.cppreference.com/w/cpp/container/vector/insert
You need to pass in an interator, so use begin and offset the position from there. No need to pass in ints by ref unless your function is going to change them. Consider checking for buffer overflow.
void SomeClass::addItem(int32_t position, const OtherClass &value)
{
assert(position < vectorOtherClass.size());
assert(position >= 0);
vectorOtherClass.insert(vectorOtherClass.begin()+position, value);
}
Why does the following code crash? And what should I do when I am iterating via reverse iterator. How do I erase individual elements then?
deque q;
q.push_back(4);
q.push_back(41);
q.push_back(14);
for (auto it = q.begin(); it != q.end();) {
auto current = it;
q.erase(current);
it++;
}
Why does the following code crash ? How do I erase individual elements then ?
std::deque::erase invalidates iterators.
All iterators and references are invalidated, unless the erased elements are at the end or the beginning of the container, in which case only the iterators and references to the erased elements are invalidated.
The past-the-end iterator is also invalidated unless the erased elements are at the beginning of the container and the last element is not erased.
In your code, the iterators to the element to be erased (i.e. it and current) will become invalid after q.erase(current), then it++ will lead to UB.
You could make use of the return value of std::deque::erase
Iterator following the last removed element. If the iterator pos refers to the last element, the end() iterator is returned.
for (auto it = q.begin(); it!=q.end(); )
{
it = q.erase(it);
}
And what should I do if I am iterating via reverse iterator.
Because std::deque::erase doesn't accept reverse_iterator as parameters, you need to use base() to convert it to normal iterator (pay attention to the position conversion). Such as
for (auto it = q.rbegin(); it!=q.rend(); )
{
it = std::make_reverse_iterator(q.erase((++it).base()));
}
As per C++11 23.3.3.4 deque modifiers /4, deque iterators become invalid if you delete certain elements.
An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements.
An erase operation that erases the first element of a deque but not the last element invalidates only the erased elements.
An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.
In your case, you're usually only ever erasing the first element so it will only invalidate that element. That means the it++ is invalid and you should instead use something like:
it = q.erase(it);
inside the loop, since the erase call itself returns an "adjusted" iterator. This will also work when removing the last element.
However, since your code is totally clearing the list (assuming it's not a cut down version of something which needs to process each element), you can ditch the loop altogether and just use:
q.clear();
As the other answerers have already pointed out, erasing elements from the queue will invalidate the iterators you are using to iterate its elements. Thus it fails.
But I assume that you don't intend to erase all elements in the queue, in which case you probably would have rather used:
q.erase(q.begin(), q.end());
or
q.clear();
Therefore, I'd like to suggest using another technique, that can be used to delete items matching certain criteria from a queue: the erase-remove idiom.
Here, the functions std::remove(...) and std::remove_if(...) are used to move the items to be deleted (matching certain criteria) to the end of the container. The range-based version of q.erase(...) is then used to delete the items.
Here's an example:
#include <deque>
#include <algorithm>
#include <iostream>
// predicate function for removal of elements
bool greater_three(int x) {
return x > 3;
}
int main() {
std::deque<int> q = {1,2,3,4,5};
for (auto i : q) std::cout << i << " "; std::cout << "\n";
// delete all items with value 3
q.erase(std::remove(q.begin(), q.end(), 3), q.end());
for (auto i : q) std::cout << i << " "; std::cout << "\n";
// delete all items with value > 3
q.erase(std::remove_if(q.begin(), q.end(), greater_three), q.end());
for (auto i : q) std::cout << i << " "; std::cout << "\n";
}
The output is:
$ g++ test.cc -std=c++11 && ./a.out
1 2 3 4 5
1 2 4 5
1 2
For reference:
http://en.cppreference.com/w/cpp/container/deque/erase
http://en.cppreference.com/w/cpp/container/deque/clear
http://en.cppreference.com/w/cpp/algorithm/remove
q clearly doesn't support removing elements while iterating through them.
Herb presents a way to loop through the vector:
for(vector<int>::iterator i = v.begin(); i < v.end(); i++) {
cout << *i << endl;
}
He replaces this code with:
copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
I am struggling to understand how, or why, this works. I looked up the copy function and the documentation says that it is equivalent to:
template<class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last,
OutputIterator result)
{
while (first!=last) {
*result = *first;
++result; ++first;
}
return result;
}
So I developed the question, "What happens when we * OutputIterator?"
reference operator*() const;
Dereference iterator
Returns *this.
And that is where I got confused. I do not see a definition of what OutputIterator is pointing to. Additionally, I do not see how the line *result = *first; could possibly translate to invoking cout << *i;
You only looked up what an OutputIterator does. OutputIterator is just a set of requirements that a bunch of types in the standard library meet. One of those types is the std::ostream_iterator, so you need to look at how this behaves in the context of std::copy.
So in the copy algorithm, we're doing *result = *first. Firstly, the operator* for std::ostream_iterator does nothing - it just returns the iterator itself. The magic happens when we assign to this iterator. If you look up std::ostream_iterator::operator=, you'll see that assigning to this iterator will insert (using <<) into the stream it was constructed with. So the assignment in your case will stream into std::cout.
After this, both result and first are incremented. Incrementing result (the std::ostream_iterator) has no effect, and incrementing first will move to the next element in the vector. Then in the next iteration, this next element is inserted into std::cout again, and so on.
As you can see, std::ostream_iterator doesn't really behave in the way that you would expect a typical iterator to behave (moving through a sequence of elements where performing indirection on them gives you the current element). However, it does meet the requirements of an OutputIterator and so can be used as one.
Here's the implementation of std::ostream_iterator::operator= from libstdc++:
/// Writes #a value to underlying ostream using operator<<. If
/// constructed with delimiter string, writes delimiter to ostream.
ostream_iterator&
operator=(const _Tp& __value)
{
__glibcxx_requires_cond(_M_stream != 0,
_M_message(__gnu_debug::__msg_output_ostream)
._M_iterator(*this));
*_M_stream << __value;
if (_M_string) *_M_stream << _M_string;
return *this;
}
Ignoring the assertion on the first line, we can see that it then inserts __value into its internal _M_stream stream. Then, if there is a delimiter set, _M_string, it also gets inserted into _M_stream. Then it returns.
Help on vector says of front()
Returns a reference to the first element in the vector container.
Unlike member vector::begin, which returns an iterator to this same element, this > function returns a direct reference.
Help on vector says of begin()
Returns an iterator referring to the first element in the vector container.
Notice that unlike member vector::front, which returns a reference to the first element, > this function returns a random access iterator.
And this code outputs:
char arr[] = { 'A', 'B', 'C' };
vector<char> vec(arr, arr+sizeof(arr));
cout << "address of vec.front() " << (void*)&vec.front() << endl;
cout << "address of vec.begin() " << (void*)&vec.begin() << endl;
address of vec.front() 00401F90
address of vec.begin() 0030F494
I don't understand what 'direct reference' means? In the case of begin() isn't a random access iterator just a pointer?
Can someone please point out the difference?
According to Stroustrup in The C++ Programming Language, Section 16.3.3; think of front() as the first element and begin() as a pointer to the first element.
In the case of begin() isn't a random access iterator just a pointer?
No, an iterator has some pointer semantics, but it's actually a class.
And even if it was, that should answer the question. It's like asking why the address of a pointer isn't the same as the address of the object it points to.
You'd get the same value if you dereference the iterator, which will give you the first element:
&(*vec.begin())
because
*vec.begin() == vec.front()
For a vector, begin() and end() return random access iterators. They might return a plain pointer; that's okay, because it meets the requirements to be a random access iterator. In particular, you can write *begin() to get a reference to the first object in the sequence (assuming there is one). front() gives you a reference to the first object in the sequence, without going through the intermediate iterator. Like this:
vector<int> v;
v.push_back(3);
int i = *v.begin(); // i == 3
int j = v.front(); // j == 3
Assuming you have at least 1 element in the vector,
vec.front()
is the same as
*vec.begin()
To keep it clear in your mind, always try to remember the following two lines assuming that you have a vector from STL called v:
v.front() = * v.begin()
&&
v.back() = * v.end()
that means if you have a vector:
v = {1,2,3,4};
and you have an iterator called IT, and you want to access the first and the last element
you can either do:
IT = v.begin();
std::cout<<*IT<<std::endl; // output : 1
IT = v.end();
std::cout<<*IT<<std::endl; // output : 4
or you can easily do this:
std::cout<<v.front<<std::endl; // output : 1
std::cout<<v.back<<std::endl; // output : 4
both will print the same output, the difference is just between if you want to use an iterator or not.