C++ comparing iterator with int - c++

Is there a simple way to compare an iterator with an int?
I have a loop like this:
for (std::vector<mystruct>::const_iterator it = vec->begin(); it != vec->end(); ++it)
Instead of looping over the entire vector, I would like to just loop over the first 3 elements. However, the following does not compile:
for (std::vector<mystruct>::const_iterator it = vec->begin(); it < 3; ++it)
It there a good way to achieve the same effect?

since it's a vector, why not just access its position directly ?
if (vec->size() > 0)
{
for (int i =0; i<3 && i< vec->size(); i++)
{
// vec[i] can be accessed directly
//do stuff
}
}

std::next(vec->begin(), 3); will be the the iterator 3 places after the first, and so you can compare to it:
for (std::vector<mystruct>::const_iterator it = vec->begin(); it != std::next(vec->begin(), 3); ++it)
Your vector will need to have at least 3 elements inside it though.

I'd want to be careful, because you can easily run into fencepost bugs.
This works on random access containers (like vector and array), but doesn't do ADL on begin because I'm lazy:
template<typename Container>
auto nth_element( Container&& c, std::size_t n )->decltype( std::begin(c) )
{
auto retval = std::begin(c);
std::size_t size = std::end(c) - retval;
retval += std::min( size, n );
return retval;
}
It returns std::end(c) if n is too big.
So you get:
for( auto it = vec->cbegin(); it != nth_element(vec, 3); ++it) {
// code
}
which deals with vectors whose size is less than 3 gracefully.
The basic core of this is that on random access iterators, the difference of iterators is ptrdiff_t -- an integral type -- and you can add integral types to iterators to move around. I just threw in a helper function, because you should only do non-trivial pointer arithmetic (and arithmetic on iterators is pointer arithmetic) in isolated functions if you can help it.
Supporting non-random access iterators is a matter of doing some traits checks. I wouldn't worry about that unless you really need it.
Note that this answer depends on some C++11 features, but no obscure ones. You'll need to #include <iterator> for std::begin and std::end and maybe <algorithm> for std::min.

Sure, you can simply go three elements past the beginning.
for (std::vector<mystruct>::const_iterator it = vec->cbegin(); it != vec->cbegin() + 3; ++it)
However, that might be error prone since you might try to access beyond the end in the case that the vector is fewer than 3 elements. I think you'd get an exception when that happens but you could prevent it by:
for(std::vector<mystruct>::const_iterator it = vec->cbegin(); it != vec->cend() && it != vec->cbegin() + 3; ++it)
Note the use of cbegin() and cend() since you asked for a const_iterator, although these are only available in c++11. You could just as easily use begin() and end() with your const_iterator.

Related

Iterators invalidation

Hi I read in C++ primer that adding elements to a vector invalidates the iterators. I don't understand why deleting elements doesn't invalidate them as the following code works
std::vector<int> a = {1,2,3,4,5,6};
auto b = a.begin();
while (b != a.end()){
if (*b%2 != 0)
a.erase(b);
else
b++;
}
NOTE: This code if from C++ primer itself and cpp reference as well
Not actually an answer to the question, but I think it is worth mentioning that in modern C++ you should try to avoid iterators by using algorithms and range-based for loops. In this particular case use std::erase_if:
std::vector<int> a = {1,2,3,4,5,6};
std::erase_if(a, [](int x) { return x%2 != 0; });
In general this code snippet
auto b = a.begin();
while (b != a.end()){
if (*b%2 != 0)
a.erase(b);
else
b++;
}
is invalid. It works because the container std::vector satisfies the concept of contiguous ranges. If instead of the vector you will use for example std::list<int> when the iterator b will be invalid.
It would be correctly to write
auto b = a.begin();
while (b != a.end()){
if (*b%2 != 0)
b = a.erase(b);
else
b++;
}
Common idiom. From cppreference: (erase) 'Invalidates iterators and references at or after the point of the erase, including the end() iterator.
Others have pointed out it should be written like this:
#include <vector>
std::vector<int> vec = { 1, 2, 3, 4 };
for (auto it = vec.begin(); it != vec.end(); )
{
if (*it % 2 != 0)
{
it = vec.erase(it);
}
else
{
++it;
}
}
Adjust if one prefers 'while' over 'for'. If performance is paramount one can start from the end though this may be less cache friendly.
Edit: code snippet is literally the cppreference link.
Adding elements to a vector may lead to a complete reallocation of the vector.
This invalidates all iterators held previously.
If you delete an entry of the vector the erase method returns:
a) a iterator to the next valid element
b) the end iterator.
But you have to use it. In your case:
b = a.erase(b);
As many pointed out it works by chance. Do not do this in prod.
Iterators are designed to be as lightweight as possible so there won't be a flag saying it's invalid. That would be too wasteful.
std::vector iterator is probably implemented as a pointer with some helper functions. Removing one element will shift everything so that same pointer now points to the new element where the old one used to be. It only works because elements are stored in contiguous memory without gaps.

What is the right way of using c++ stl iterators instead of traditional pointers?

I have the following very basic question. I want to use stl iterators instead of traditional C-type pointers for filling an array in a function. By the C-style way I mean the following example:
void f(double* v, size_t n) {
for (int i = 0; i < n; i++)
v[i] = 10; /* a more reasonable value in practice! */
}
I would convert this to the C++ style using the iterators as follows:
void f(vector<double>::const_iterator first, vector<double>::const_iterator last) {
for(vector<double>::iterator it = first; it != last; it++)
*it = 10;
}
But I get compilation errors. If I use iterator instead of const_iterator the problem will be solved. However, I was wondering if that is the correct way? Because I thought vector.begin() and vector.end() iterators are constant.
Thanks in advance!
The difference between
const vector<double>::iterator
and
vector<double>::const_iterator
is roughly the same as between double * const v and const double *v:
the first says that the iterator must remain constant, but what it points to can be changed
the second says that the iterator itself is changeable, but what it points to is const.
If you rewrite the function as
void f(const vector<double>::iterator first, const vector<double>::iterator last) {
for(vector<double>::iterator it = first; it != last; it++)
*it = 10;
}
it would compile and run correctly.
What you see is due to the fact that const_iterator's correspond roughly to pointers to const. So you can change the value of the iterator, i.e. make it point somewhere else, but you cannot modify what it points to.
This is different from const iterators, which would not allow incrementing or decrementing them. Here is an example:
#include <vector>
int main() {
std::vector<int> v{ 1, 2, 3 };
std::vector<int>::const_iterator i = v.begin();
*i = 10; // ERROR!
++i; // OK
std::vector<int>::iterator const ci = v.begin();
*ci = 10; // OK
++ci; // ERROR!
}
std::fill(my_vector.begin(), my_vector.end(), 10);
The problem is that your functions takes const_iterators but your loop needs an iterator, since you want to modify the data. The solution is of course to let your function take iterators right away, since it is obviously meant to modify the range.
This doesn't have anything to do with what vector.begin() returns. For a const object or reference they will return const_iterators, otherwise they'll return iterators. But your function definitely needs iterators, since it modifies the values in the range passed to it.
Since you're using const_iterators, you can't modify the vector. Using non-const iterators is the right thing to do.
In answer to your last question, vector.begin() and vector.end() have both const_ and non-const_ implementations. If your vector is non-const, you'll get a non-const_ iterator. See the documentation for std::vector::begin.

erase element from vector

I have the following vector passed to a function
void WuManber::Initialize( const vector<const char *> &patterns,
bool bCaseSensitive, bool bIncludeSpecialCharacters, bool bIncludeExtendedAscii )
I want to erase any element that is less in length than 2
I tried the following but it didn't compile even
can you tell me what I am missing here.
for(vector<const char *>::iterator iter = patterns.begin();iter != patterns.end();iter++)
{//my for start
size_t lenPattern = strlen((iter).c_str);
if ( 2 > lenPattern )
patterns.erase(iter);
}//my for end
On top of the problems others have pointed out, it's a bad idea to erase items from the vector as you iterate over it. There are techniques to do it right, but it's generally slow and fragile. remove_if is almost always a better option for lots of random erasures from a vector:
#include <algorithm>
bool less_than_two_characters(const char* str) { return strlen(str) < 2; }
void Initialize(vector<const char*>& v) {
v.erase(std::remove_if(v.begin(), v.end(), less_than_two_characters), v.end());
}
In C++0x you can do that more concisely with a lambda function but the above is more likely to work on a slightly older compiler.
This cannot work, because if you erase something from your vector you invalidate your iterator.
It probably does not compile because you use your iterater in a wrong way. You might try iter->c_str or (*iter).c_str. On the other hand, give us the error message ;)
Next thing, you try to modify a const vector. This is why the compiler is complaining.
You could do this with an index, like this:
for (int i = 0; i < patterns.size(); ++i) {
size_t lenPattern = strlen(patterns[i]);
if (2 > lenPattern) {
patterns.erase(patterns.begin() + i);
--i;
}
}
However, this is not very elegant, as I manipulate the counter...
First, as Tim mentioned, the patterns parameter is a const reference, so the compiler won't let you modify it - change that if you want to be able to erase elements in it.
Keep in mind that iter 'points to' a pointer (a char const* to be specific). So you dereference the iterator to get to the string pointer:
size_t lenPattern = strlen(*iter);
if ( 2 > lenPattern )
iter = patterns.erase(iter);
Also, in the last line of the snippet, iter is assigned whatever erase() returns to keep it a valid iterator.
Note that erasing the element pointed to by iter will not free whatever string is pointed to by the pointer in the vector. It's not clear whether or not that might be necessary, since the vector might not 'own' the strings that are pointed to.

c++ best way to use for loop

I have this question that runs in my mind...
I have a std::vector to iterate:
which is the best way (the faster) to iterate?
here is the code using an iterator:
// using the iterator
for( std::vector <myClass*>::iterator it = myObject.begin( ); it != myObject.end( ); it++ )
{
(*it)->someFunction( );
}
and here is 'normal' mode...
// normal loop
for( int i = 0; i < myObject.Size( ); i++ )
{
myObject[i]->someFunction( );
}
thanks for your suggestions!
None of the two will be any faster really, because on most implementations a vector<T>::iterator is just a typedef for T* and size is cached.
But doing ++it instead of it++ is a good habit. The latter involves creating a temporary.
for(std::vector <myClass*>::iterator it = myObject.begin( );
it != myObject.end( );
++it)
^^^^
On other containers such as map, list etc. with nontrivial iterators the difference between postincrement and preincrement might become noticable.
If you really care, you can find out: Just make a single source file with one function with that loop and look at the optimized assembly:
g++ -O2 -S -o ver1.s ver1.cpp
g++ -O2 -S -o ver2.s ver2.cpp
You can directly see the differences! I bet there are none.
That said, you should use the iterator pattern because it's idomatic, generic C++ and it gets you in the right mood -- plus, it works in far more general cases than just vectors! Write it like this:
typedef std::vector<MyClass*> myVec;
for (myVec::const_iterator it = v.begin(), end = v.end(); it != end; ++it)
{
const MyClass & x = **it;
/* ... */
}
In case you're curious, a vector iterator is most likely just going to be a native, raw pointer, so there's really nothing to fear in terms of efficiency, and a lot to be enjoyed from the self-explanatory, algorithmic style!
PS If you have C++0x, say it like this:
for (auto it = v.cbegin(), end = v.cend(); it != end; ++it)
// or
for (const MyClass * & i : v)
The first code will decompose into incrementing a pointer. The second one will increment an index, and index into the array. The first one can use slightly smaller instructions (and thus potentially be faster) assuming the compiler doesn't optimize the second into the first already. But it will be a trivial difference.
Iterators should be preferred, however, not because of speed but because you can then easily move the code to iterate any standard C++ container, not just vector.
However, you've got a few things to improve.
Don't use it++, but ++it. This can be very important in C++ because iterators can end up doing a little more work in post-increment which won't be optimized out as if the type were an int.
Don't constantly call end() or size(). For some iterator types and collections this might not be optimized out and can be very sub-optimal.
Use vector::size_type when you need an index into a vector. int is not guaranteed to be big enough, while size_type was made specifically for that.
So, the better ways to write these are:
// using the iterator
for(std::vector <myClass*>::iterator it = myObject.begin( ), end = myObject.end(); it != end; ++it)
{
(*it)->someFunction( );
}
// normal loop
for(std::vector <myClass*>::size_type i = 0, size = myObject.size(); i < size; ++i)
{
myObject[i]->someFunction( );
}
Here's what I use:
int s = vec.size();
for(int i=0;i<s;i++)
{
T &o = vec[i];
...
}
This loop has the following advantages over other approaches:
it always looks the same (unlike the hacks that change from i++ to ++i)
it's relatively short to write (compared to the iterator version)
the int indexes are useful in your public interface (unlike iterators)
it still always looks the same (unlike stdlib algorithms, where you need documentation to remember the parameters)
it's very old and thus widely used. (originally comes from book "The C programming language" aka K&R)
It doesn't give warnings on most compilers (unlike the loop that was used in the question)
It does have some disadvantages too:
some newer programmers doesn't like it because they think C ways are too old
in a const function, you might need to change it slightly to const T &o = vec[i]; or change the data member mutable
type T changes depending on the use
You can do this:
#include <iostream>
using namespace std;
int main() {
for (int i = 3; i > 0; i--) {
cout << i << "\n";
}
return 0;
}
source https://mockstacks.com/Cpp_For_Loop

Does iterator support + operator?

I saw the following code used to delete one selected element from std::vector:
vector<hgCoord>::iterator it;
int iIndex = 0;
const int iSelected = 5;
for( it = vecPoints.begin(); it != vecPoints.end(); ++it, ++iIndex )
{
if( iIndex == iSelected )
{
vecPoints.erase( it );
break;
}
}
I argue that this code is not efficient and should be written as follows:
vector<hgCoord>::iterator it;
int iIndex = 0;
const int iSelected = 5; // we assume the vector has more than 5 elements.
vecPoints.erase( vecPoints.begin() + iSelected );
However, I am not sure whether or not this code is following the C++ STL standard.
To make this code generic, so it works no matter whether the iterator supports operator +, and uses the most efficient available implementation:
template <typename C>
void erase_at(C& container, typename C::size_type index) {
typename C::iterator i = container.begin();
std::advance(i, index);
container.erase(i);
}
Internally, std::advance uses operator + if the iterator type supports it. Otherwise (e.g. for std::list<>::iterator) it advances the iterator one step at a time in a loop, just like the first code you posted.
Random-access iterators support addition and subtraction, and std::vector iterators are random-access.
You argue correctly :)
That should work fine for a vector because vector iterators are random access iterators, so it's OK to add an offset as you have done. The same won't work for some other container types (such as deque or map).
So, your code is better for a vector, but the other code may have been intended to work with other types of container.