C++ STL Vectors: Get iterator from index? - c++

So, I wrote a bunch of code that accesses elements in an stl vector by index[], but now I need to copy just a chunk of the vector. It looks like vector.insert(pos, first, last) is the function I want... except I only have first and last as ints. Is there any nice way I can get an iterator to these values?

Try this:
vector<Type>::iterator nth = v.begin() + index;

way mentioned by #dirkgently ( v.begin() + index ) nice and fast for vectors
but std::advance( v.begin(), index ) most generic way and for random access iterators works constant time too.
EDIT
differences in usage:
std::vector<>::iterator it = ( v.begin() + index );
or
std::vector<>::iterator it = v.begin();
std::advance( it, index );
added after #litb notes.

Also; auto it = std::next(v.begin(), index);
Update: Needs a C++11x compliant compiler

You can always use std::advance to move the iterator a certain amount of positions in constant time:
std::vector<int>::iterator it = myvector.begin();
std::advance(it, 2);

Actutally std::vector are meant to be used as C tab when needed. (C++ standard requests that for vector implementation , as far as I know - replacement for array in Wikipedia)
For instance it is perfectly legal to do this folowing, according to me:
int main()
{
void foo(const char *);
sdt::vector<char> vec;
vec.push_back('h');
vec.push_back('e');
vec.push_back('l');
vec.push_back('l');
vec.push_back('o');
vec.push_back('/0');
foo(&vec[0]);
}
Of course, either foo must not copy the address passed as a parameter and store it somewhere, or you should ensure in your program to never push any new item in vec, or requesting to change its capacity. Or risk segmentation fault...
Therefore in your exemple it leads to
vector.insert(pos, &vec[first_index], &vec[last_index]);

Related

STL algorithm to repeat an erase operation on a second container?

Life gave me the following objects:
std::vector<T1> v1;
std::vector<T2> v2;
typename std::vector<T1>::iterator it_first;
typename std::vector<T1>::iterator it_last;
and the following constraints:
v1.size() == v2.size() > 0
v1.begin() <= it_first <= it_last <= v1.end()
Removing from v1 the range pointed by the two iterators is a trivial single line, but how do I remove the same range also from v2?
I can easily solve this for instance by building v2 iterators using a mix of std::distance/advance, but I was wondering if the STL provides some machinery for this. Something like the erase-remove idiom coupled with a transform operation, maybe? It seems beyond my STL-fu...
When you have an iterator, then you can get the index via std::distance to begin().
When you have an index, then you can get the iterator via begin() + index.
Thats bascially all you need to get the same range of incdices in the second vector.
Btw iterators are to abstract away the index. When you need the index, then work with the index. For erase that would be
size_t start = 1;
size_t end = 42;
std::erase( v1.begin() + start, v1.begin() + end );
std::erase( v2.begin() + start, v2.begin() + end );
I can easily solve this for instance by building v2 iterators using a mix of std::distance/advance, but I was wondering if the STL provides some machinery for this. Something like the erase-remove idiom coupled with a transform operation, maybe? It seems beyond my STL-fu...
I saw the edit only after writing the answer. The thing is std::distance / std::advance is the machinery to switch between iterators and indices.
Consider that iterators in general are for things that can be iterated. You do not even necesarily need a container to iterate. For example you could write an iterator type that lets you iterate integers. This iterator type could be used like this:
std::vector<std::string> x(100);
auto begin = int_iterator(0);
auto end = int_iterator(100);
for ( ; begin != end; ++begin) std::cout << x[ *begin ];
This is no C-ism. The example is perhaps not the best, but the int_iterator itself is on par with the general concept of iterators.
Usually iterators have no knowledge about the underlying container (if there is one). There are few exceptions (eg insert_iterator). And in case of erase of course you need to pass iterators that refer to element in the corresponding container.

Finding length of range in C++

I have defied a range and I need to find the number of elements in it. My current code is
size_t c = 0;
for(auto elem : range){
c++;
}
return c;
However, the compiler is whining about the unused variable elem and I can't get rid of it. I though of using something like
std::count_if(range.begin(), range.end(), [](type elem){return ture;});
But I feel it is an overkill and it does not seem right.
I am wondering if there is a nicer systematic way of achieving this without defining an extra variable?
If (for some reason) your range doesn't have a size() member function, you can use std::distance. This should always work since a range is required to have a begin and end iterator.
std::distance(cbegin(range), cend(range));
You can use std::distance like
std::distance(range.begin(), range.end());
Returns the number of hops from first to last.
And note the complexity:
Complexity
Linear.
However, if InputIt additionally meets the requirements of
LegacyRandomAccessIterator,
complexity is constant.
In C++ all the containers implement size method of a constant complexity so if by range you consider a container then there is no need for reinventing the wheel.
However, you can also use std::distance if you want to determine the number of elements from a certain range like:
std::vector<int> v{ 3, 1, 4 };
std::cout << std::distance(v.begin(), v.end() << std::endl; // 3
If you take a look at the possible implementation of std::distance, it is something like
while (first != last) {
++first;
++n;
}
where first and the last are start and end point of you range, and n is a simple counter.

Clamp iterator value to end() with std::min

I have a vector with n Strings in it. Now lets say I want to "page" or "group" those strings in a map.
typedef std::vector<std::string> TStringVec;
TStringVec myVec;
//.. fill myVecwith n elements
typedef std::map<int, TStringVec>> TPagedMap;
TPagedMap myMap;
int ItemsPerPage = 3 // or whatever
int PagesRequired = std::ceil(myVec.size() / nItemsPerPage);
for (int Page = 0; Page < NumPagesMax; ++Page)
{
TStringVec::const_iterator Begin = myVec.begin() + (ItemsPerPage * Page);
TStringVec::const_iterator End = myVec.begin() + ItemsPerPage * (Page+1);
myMap[Page] = TStringVec(Begin, End);
}
One can easily spot the problem here. When determining the end iterator, I risk leaving the allocated space by the vector.
Quick example: 5 elements in the vector, ItemsPerPage is 3. That means we need a total of 2 pages in the map to group all elements.
Now when hitting the last iteration, begin is pointing at myVec[3] but end is "pointing" to myVec[6]. Remember, myVec only has 5 elements.
Could this case be safely handled by swapping
TStringVec::const_iterator End = myVec.begin() + ItemsPerPage * (Page+1);
with
TStringVec::const_iterator End = std::min(myVec.begin() + ItemsPerPage * (Page+1), myVec.end());
It compiles of course, and it seems to work. But I'm not sure if this can be considered a safe thing to do. Any advice or a definitive answer?
I think the question is... Is a value "past" .end() guaranteed to be larger than the adress returned by .end()?
Thanks in advance.
EDIT: of course, a if check beforehand could solve the problem, but I'm looking for a more elegant solution.
There are two things wrong with your proposed replacement:
You are potentially creating an iterator past the end-iterator, which is UB.
For some unfathomable reason, you want to stop at end() - 1?? Everywhere else, you properly use half-open ranges.
What you want is more like
auto End = myVec.cbegin() + std::min(ItemsPerPage * (Page + 1), myVec.size());
Also take note that I used auto to avoid needlessly specifying complicated type-names.
As an aside, using std::ceil on an integer is not very useful conceptually, at least the compiler will likely optimize out the round-trip through double.
With range-v3, you may use chunk view:
std::vector<std::string> myVec = /*...*/;
const int ItemsPerPage = 3 // or whatever
std::map<int, std::vector<std::string>>> myMap;
int counter = 0;
for (const auto& page : myVec | ranges::view::chunk(ItemsPerPage)) {
myMap[counter++] = page;
}
Demo

Using iterators as offset index

Apologies if this was already asked. I couldn't find an answer to that.
Is it legal to use an iterator as an offset index?
For example:
for (list<T> iterator::it = v2->vals.begin(); it!=v2->vals.end();++i) {
v3.push_back(v2[it] + v1[it]);
}
where : const Vec& v2
and vals is a list in Vec class's protected.
Many thanks!
You can't directly use iterators as indexes.
However, if you do want to use the iterators "position" in its container to have the index you want, you can do so with std::distance, i.e.:
unsigned int index = std::distance(v2.begin(), it);
//if it was the fifth element, index's value is 4.
If v1 and v2 are instances of a custom data type, then yes it's possible, because you can then create an overloaded operator[] functions which takes the correct iterator types as arguments.
However it's not as easy as it seems for you, since at least v2 seems to be a pointer, so you need to either dereference it first:
(*v2)[it]
or call the operator function explicitly:
v2->operator[](it)
For parallel traversal of multiple containers, you can use Boost.Iterator's Zip Iterator :
std::transform(
boost::make_zip_iterator(boost::make_tuple(begin(v1), begin(v2))),
boost::make_zip_iterator(boost::make_tuple( end(v1), end(v2))),
std::back_inserter(v3),
[](boost::tuple<int const &, int const&> const &p) {
return p.get<0>() + p.get<1>();
}
);
I'm admittedly not really convinced by the syntax though. IMHO this library would benefit from some C++11/14 peppering.

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.