Specifying input and output ranges to algorithms - c++

There are a number of use cases in the standard library, and I have run into my own code, situations where I want to pass an input and an output range that must be the same size, often to some algorithm. At present this requires three, or four if you want to be careful, iterators. Often there is then a bunch of checking to make sure the iterators make sense.
I am aware that array_view might, in some cases, coalesce pairs of being/end iterators but that still leaves checks required that the input and output array_views might be different size. Has there been any discussion on a class to contain both the input and the output range specifications? Maybe the range proposal solves some or all of this of this but I'm not clear how it does.

I often have a similar use case. I have found that the most versatile method is to pass a reference to an output 'container' concept rather than a range or pair of iterators. Then the algorithm is free to resize or extend the container as necessary and the possibility of buffer overruns disappears.
for (a simple contrived) example, something like:
template<class InputIter, class OutputContainer>
void encrypt_to(OutputContainer& dest, InputIter first, InputIter last)
{
dest.resize(last - first);
// ... todo: replace this with some actual encryption
std::copy(first, last, dest.begin());
}
For functions where I want to hide the implementation, I have an abstract interface representing the destination container. It needs virtual begin(), end() and resize() methods.
for example:
#include <iostream>
#include <algorithm>
#include <vector>
struct mutable_byte_buffer_concept {
using iterator=uint8_t*;
virtual iterator begin() = 0;
virtual iterator end() = 0;
virtual void resize(size_t newsize) = 0;
// note : no virtual destructor - by design.
};
template<class Container>
struct mutable_byte_buffer_model : mutable_byte_buffer_concept
{
mutable_byte_buffer_model(Container& container)
: container(container)
{
}
virtual iterator begin() override
{
return reinterpret_cast<iterator>(&container[0]);
}
virtual iterator end() override
{
return reinterpret_cast<iterator>(&container[0]) + container.size();
}
virtual void resize(size_t newsize) override
{
container.resize(newsize);
}
Container& container;
};
template<class Container>
auto make_mutable_buffer(Container& c)
-> mutable_byte_buffer_model<Container>
{
return { c };
}
// note: accepting an rvalue allows me to use polymorphism at the call site without needing any memory allocation
template<class Iter>
void copy_to(mutable_byte_buffer_concept&& dest, Iter first, Iter last)
{
dest.resize(last - first);
std::copy(first, last, dest.begin());
}
using namespace std;
auto main() -> int
{
auto a = "Hello, World"s;
string b;
vector<uint8_t> c;
string d;
copy_to(make_mutable_buffer(b), begin(a), end(a));
copy_to(make_mutable_buffer(c), begin(b), end(b));
copy_to(make_mutable_buffer(d), begin(c), end(c));
cout << d << endl;
return 0;
}

Related

How does std::reverse_iterator hold one before begin?

This is a code example using std::reverse_iterator:
template<typename T, size_t SIZE>
class Stack {
T arr[SIZE];
size_t pos = 0;
public:
T pop() {
return arr[--pos];
}
Stack& push(const T& t) {
arr[pos++] = t;
return *this;
}
auto begin() {
return std::reverse_iterator(arr+pos);
}
auto end() {
return std::reverse_iterator(arr);
// ^ does reverse_iterator take this `one back`? how?
}
};
int main() {
Stack<int, 4> s;
s.push(5).push(15).push(25).push(35);
for(int val: s) {
std::cout << val << ' ';
}
}
// output is as expected: 35 25 15 5
When using std::reverse_iterator as an adaptor for another iterator, the newly adapted end shall be one before the original begin. However calling std::prev on begin is UB.
How does std::reverse_iterator hold one before begin?
Initialization of std::reverse_iterator from an iterator does not decrease the iterator upon initialization, as it would then be UB when sending begin to it (one cannot assume that std::prev(begin) is a valid call).
The trick is simple, std::reverse_iterator holds the original iterator passed to it, without modifying it. Only when it is being dereferenced it peeks back to the actual value. So in a way the iterator is pointing inside to the next element, from which it can get the current.
It would look something like:
// partial possible implementation of reverse_iterator for demo purpose
template<typename Itr>
class reverse_iterator {
Itr itr;
public:
constexpr explicit reverse_iterator(Itr itr): itr(itr) {}
constexpr auto& operator*() {
return *std::prev(itr); // <== only here we peek back
}
constexpr auto& operator++() {
--itr;
return *this;
}
friend bool operator!=(reverse_iterator<Itr> a, reverse_iterator<Itr> b) {
return a.itr != b.itr;
}
};
This is however an internal implementation detail (and can be in fact implemented in other similar manners). The user of std::reverse_iterator shall not be concerned with how it is implemented.

Recasting a container of void pointers

Short version
Can I reinterpret_cast a std::vector<void*>* to a std::vector<double*>*?
What about with other STL containers?
Long version
I have a function to recast a vector of void pointers to a datatype specified by a template argument:
template <typename T>
std::vector<T*> recastPtrs(std::vector<void*> const& x) {
std::vector<T*> y(x.size());
std::transform(x.begin(), x.end(), y.begin(),
[](void *a) { return static_cast<T*>(a); } );
return y;
}
But I was thinking that copying the vector contents isn't really necessary, since we're really just reinterpreting what's being pointed to.
After some tinkering, I came up with this:
template <typename T>
std::vector<T*> recastPtrs(std::vector<void*>&& x) {
auto xPtr = reinterpret_cast<std::vector<T*>*>(&x);
return std::vector<T*>(std::move(*xPtr));
}
So my questions are:
Is it safe to reinterpret_cast an entire vector like this?
What if it was a different kind of container (like a std::list or std::map)? To be clear, I mean casting a std::list<void*> to std::list<T*>, not casting between STL container types.
I'm still trying to wrap my head around move semantics. Am I doing it right?
And one follow-up question: What would be the best way to generate a const version without code duplication? i.e. to define
std::vector<T const*> recastPtrs(std::vector<void const*> const&);
std::vector<T const*> recastPtrs(std::vector<void const*>&&);
MWE
#include <vector>
#include <algorithm>
#include <iostream>
template <typename T>
std::vector<T*> recastPtrs(std::vector<void*> const& x) {
std::vector<T*> y(x.size());
std::transform(x.begin(), x.end(), y.begin(),
[](void *a) { return static_cast<T*>(a); } );
return y;
}
template <typename T>
std::vector<T*> recastPtrs(std::vector<void*>&& x) {
auto xPtr = reinterpret_cast<std::vector<T*>*>(&x);
return std::vector<T*>(std::move(*xPtr));
}
template <typename T>
void printVectorAddr(std::vector<T> const& vec) {
std::cout<<" vector object at "<<&vec<<", data()="<<vec.data()<<std::endl;
}
int main(void) {
std::cout<<"Original void pointers"<<std::endl;
std::vector<void*> voidPtrs(100);
printVectorAddr(voidPtrs);
std::cout<<"Elementwise static_cast"<<std::endl;
auto dblPtrs = recastPtrs<double>(voidPtrs);
printVectorAddr(dblPtrs);
std::cout<<"reintepret_cast entire vector, then move ctor"<<std::endl;
auto dblPtrs2 = recastPtrs<double>(std::move(voidPtrs));
printVectorAddr(dblPtrs2);
}
Example output:
Original void pointers
vector object at 0x7ffe230b1cb0, data()=0x21de030
Elementwise static_cast
vector object at 0x7ffe230b1cd0, data()=0x21de360
reintepret_cast entire vector, then move ctor
vector object at 0x7ffe230b1cf0, data()=0x21de030
Note that the reinterpret_cast version reuses the underlying data structure.
Previously-asked questions that didn't seem relevant
These are the questions that come up when I tried to search this:
reinterpret_cast vector of class A to vector of class B
reinterpret_cast vector of derived class to vector of base class
reinterpret_cast-ing vector of one type to a vector of another type which is of the same type
And the answer to these was a unanimous NO, with reference to the strict aliasing rule. But I figure that doesn't apply to my case, since the vector being recast is an rvalue, so there's no opportunity for aliasing.
Why I'm trying to do this
I'm interfacing with a MATLAB library that gives me data pointers as void* along with a variable indicating the datatype. I have one function that validates the inputs and collects these pointers into a vector:
void parseInputs(int argc, mxArray* inputs[], std::vector<void*> &dataPtrs, mxClassID &numericType);
I can't templatize this part since the type is not known until runtime. On the other side, I have numeric routines to operate on vectors of a known datatype:
template <typename T>
void processData(std::vector<T*> const& dataPtrs);
So I'm just trying to connect one to the other:
void processData(std::vector<void*>&& voidPtrs, mxClassID numericType) {
switch (numericType) {
case mxDOUBLE_CLASS:
processData(recastPtrs<double>(std::move(voidPtrs)));
break;
case mxSINGLE_CLASS:
processData(recastPtrs<float>(std::move(voidPtrs)));
break;
default:
assert(0 && "Unsupported datatype");
break;
}
}
Given the comment that you're receiving the void * from a C library (something like malloc), it seems like we can probably narrow the problem down quite a bit.
In particular, I'd guess you're really dealing with something that's more like an array_view than a vector. That is, you want something that lets you access some data cleanly. You might change individual items in that collection, but you'll never change the collection as a whole (e.g., you won't try to do a push_back that could need to expand the memory allocation).
For such a case, you can pretty easily create a wrapper of your own that gives you vector-like access to the data--defines an iterator type, has a begin() and end() (and if you want, the others like rbegin()/rend(), cbegin()/cend() and crbegin()/crend()), as well as an at() that does range-checked indexing, and so on.
So a fairly minimal version could look something like this:
#pragma once
#include <cstddef>
#include <stdexcept>
#include <cstdlib>
#include <iterator>
template <class T> // note: no allocator, since we don't do allocation
class array_view {
T *data;
std::size_t size_;
public:
array_view(void *data, std::size_t size_) : data(reinterpret_cast<T *>(data)), size_(size_) {}
T &operator[](std::size_t index) { return data[index]; }
T &at(std::size_t index) {
if (index > size_) throw std::out_of_range("Index out of range");
return data[index];
}
std::size_t size() const { return size_; }
typedef T *iterator;
typedef T const &const_iterator;
typedef T value_type;
typedef T &reference;
iterator begin() { return data; }
iterator end() { return data + size_; }
const_iterator cbegin() { return data; }
const_iterator cend() { return data + size_; }
class reverse_iterator {
T *it;
public:
reverse_iterator(T *it) : it(it) {}
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T *;
using reference = T &;
reverse_iterator &operator++() {
--it;
return *this;
}
reverse_iterator &operator--() {
++it;
return *this;
}
reverse_iterator operator+(size_t size) const {
return reverse_iterator(it - size);
}
reverse_iterator operator-(size_t size) const {
return reverse_iterator(it + size);
}
difference_type operator-(reverse_iterator const &r) const {
return it - r.it;
}
bool operator==(reverse_iterator const &r) const { return it == r.it; }
bool operator!=(reverse_iterator const &r) const { return it != r.it; }
bool operator<(reverse_iterator const &r) const { return std::less<T*>(r.it, it); }
bool operator>(reverse_iterator const &r) const { return std::less<T*>(it, r.it); }
T &operator *() { return *(it-1); }
};
reverse_iterator rbegin() { return data + size_; }
reverse_iterator rend() { return data; }
};
I've tried to show enough that it should be fairly apparent how to add most of the missing functionality (e.g., crbegin()/crend()), but I haven't worked really hard at including everything here, since much of what's left is more repetitive and tedious than educational.
This is enough to use the array_view in most of the typical vector-like ways. For example:
#include "array_view"
#include <iostream>
#include <iterator>
int main() {
void *raw = malloc(16 * sizeof(int));
array_view<int> data(raw, 16);
std::cout << "Range based:\n";
for (auto & i : data)
i = rand();
for (auto const &i : data)
std::cout << i << '\n';
std::cout << "\niterator-based, reverse:\n";
auto end = data.rend();
for (auto d = data.rbegin(); d != end; ++d)
std::cout << *d << '\n';
std::cout << "Forward, counted:\n";
for (int i=0; i<data.size(); i++) {
data[i] += 10;
std::cout << data[i] << '\n';
}
}
Note that this doesn't attempt to deal with copy/move construction at all, nor with destruction. At least as I've formulated it, the array_view is a non-owning view into some existing data. It's up to you (or at least something outside of the array_view) to destroy the data when appropriate. Since we're not destroying the data, we can use the compiler-generated copy and move constructors without any problem. We won't get a double-delete from doing a shallow copy of the pointer, because we don't do any delete when the array_view is destroyed.
No, you cannot do anything like this in Standard C++.
The strict aliasing rule says that to access an object of type T, you must use an expression of type T; with a very short list of exceptions to that.
Accessing a double * via a void * expression is not such an exception; let alone a vector of each. Nor is it an exception if you accessed the object of type T via an rvalue.

Reversing range-based for loop on a custom container class

I'm trying to improve my C++ skills by porting the major examples in Algorithms, 4th Edition by Sedgewick and Wayne. I wrote a generic stack implementation based on their Java example.
My stack works fine, but I'd like to make a performance improvement and got stuck trying to write a reverse iterator.
template<typename T> class ResizingArrayStack {
public:
T* begin() { return &array_ptr[0]; }
T* end() { return &array_ptr[N]; }
...
// Here we're iterating forward through the array, with an unused variable `i`.
// It would be nice performance-wise to iterate in reverse without calling pop(), and without triggering a resize.
for ( auto& i : lifo_stack ) {
cout << "Current loop iteration has i = " << i << endl;
}
// // Alternatively, pop from the stack N times.
// cout << "Popped an item from the stack: " << lifo_stack.pop() << endl;
I tried switching the begin and end member functions above, but found that the expanded for-loop always increments with ++__begin, even if __end is at a lower memory address. How can we get i to loop in reverse (LIFO with respect to the stack)?
Please feel free to comment on my code style if there are egregious errors or aspects that look out of date. I want stay in-line with good 'modern' C++.
If you want to use the range-for loop with reverse iterators, you can use a wrapper class Reverse that stores a range and returns the reverse_iterators corresponding to begin and end
#include <iostream>
#include <iterator>
#include <vector>
template<class Rng>
class Reverse
{
Rng const& rng;
public:
Reverse(Rng const& r) noexcept
:
rng(r)
{}
auto begin() const noexcept { using std::end; return std::make_reverse_iterator(end(rng)); }
auto end() const noexcept { using std::begin; return std::make_reverse_iterator(begin(rng)); }
};
int main()
{
std::vector<int> my_stack;
my_stack.push_back(1);
my_stack.push_back(2);
my_stack.push_back(3);
// prints 3,2,1
for (auto const& elem : Reverse(my_stack)) {
std::cout << elem << ',';
}
}
Live Example
Note that this uses C++1z template deduction, only supported by g++ 7.0 SVN and clang 5.0 SVN. For earlier compilers you could add a helper function
template<class Rng>
auto MakeReverse(Rng const& rng) { return Reverse<Rng>(rng); }
for (auto const& elem : MakeReverse(my_stack)) {
std::cout << elem << ',';
}
Live Example (works as of gcc 5.1 or clang 3.5)
Alternatively, you can use the Boost.Range library and simply do (will work any C++11 compiler)
#include <iostream>
#include <vector>
#include <boost/range/adaptor/reversed.hpp>
int main()
{
std::vector<int> my_stack;
my_stack.push_back(1);
my_stack.push_back(2);
my_stack.push_back(3);
for (auto const& elem : boost::adaptors::reverse(my_stack)) {
std::cout << elem << ',';
}
}
Live Example
Note that you have to be careful about passing temporary variables to such adaptors, both mine and the Boost adaptor do not work when passing e.g. a raw std::vector<int>{3,2,1}, as pointed out by #Pixelchemist in the comments.
Here a scratch for your problem. Don't consider this as working code. Use it to just get an idea of how reverse iterator MAY be implemented (just one many possible ways).
template<typename T> class ResizingArrayStack {
public:
class reverse_iterator
{
ResizingArrayStack & _storage;
int _pointer;
public:
inline reverse_iterator(ResizingArrayStack & storage,
int pointer)
: _storage(storage)
, _pointer(pointer)
{}
inline reverse_iterator & operator++() // prefix
{
--_pointer;
return *this;
}
inline reverse_iterator operator++() // postfix
{
reverse_iterator tmp(*this);
--_pointer;
return tmp;
}
inline T & operator*()
{
return _storage.getByIndex(_pointer);
}
// TODO: == != etc
};
reverse_iterator rbegin() { return reverse_iterator(*this, N - 1); }
reverse_iterator rend() { return reverse_iterator(*this, -1); }
// ... //
};
once you have functioning (regular) iterators,
implement reverse iterators using the standard library
helper class template std::reverse_iterator
#include <iterator>
class XX {
// your code
typedef std::reverse_iterator<iterator> reverse_iterator;
reverse_iterator rbegin() { return reverse_iterator{end()}; }
reverse_iterator rend() { return reverse_iterator{begin()}; }
Looking at your full codelifo_stack.pop() invalidates your iterators, so it cannot be used inside a ranged for. You have Undefined Behavior
Moreover it doesn't make much sense to use a range for for a stack. If you can iterate over its elements then it's not a stack now isn't it? A stack has the property that you can only access the most recent inserted element.
Based on your comment:
Consider the case where you add items slowly and individually, but
wish to dump them out of the stack as quickly as possible. I don't
want the overhead of copying and resizing arrays which pop() would
trigger at that moment.
I still think that ranged-for does not make sense for a stack.
Here is how I see your problem solved:
lifo_stack.disable_resizing(); // prevents resizing
while (!lifo_stack.is_empty()
{
lifo_stack.pop(); // maybe use the poped element
}
lifo_stack.enable_resizing(); // re-enables resizing and triggers a resize
If you don't need the popped elements and just want to emtpy the stack, there is a faster way (based on your class implementation):
// empties the stack
void clear()
{
delete[] array_ptr;
array_ptr = new T[1];;
max_size = 1;
N = 0;
}
One last final though if you want to use modern C++ then use unique_ptr instead of manual new and delete. It is easier but most importantly safer. And read on the rule of 0/3/5.
This solution does not introduce unnecessary copies and does not exhibit incorrect forwarding as suggested by some comments. Explanation below.
You can use some wrapper which has begin and end functions that actually
return reverse iterators.
template<class T>
struct revert_wrapper
{
T o;
revert_wrapper(T&& i) : o(std::forward<T>(i)) {}
};
template<class T>
auto begin(revert_wrapper<T>& r)
{
using std::end;
return std::make_reverse_iterator(end(r.o));
}
template<class T>
auto end(revert_wrapper<T>& r)
{
using std::begin;
return std::make_reverse_iterator(begin(r.o));
}
template<class T>
auto begin(revert_wrapper<T> const& r)
{
using std::end;
return std::make_reverse_iterator(end(r.o));
}
template<class T>
auto end(revert_wrapper<T> const& r)
{
using std::begin;
return std::make_reverse_iterator(begin(r.o));
}
template<class T>
auto reverse(T&& ob)
{
return revert_wrapper<T>{ std::forward<T>(ob) };
}
Used like this:
std::vector<int> v{1, 2, 3, 4};
for (auto i : reverse(v))
{
std::cout << i << "\n";
}
or in your case
for ( auto& i : reverse(lifo_stack) ) {
cout << "Current loop iteration has i = " << i << endl;
cout << "Popped an item from the stack: " << lifo_stack.pop() << endl;
}
Since fowarding is not an easy topic and there is misconception around I'll further explain some details. I'll use std::vector<int> as an example for the "to be reversed" type T.
1. The function template reverse.
1.1 Passing an lvalue std::vector<int>:
std::vector<int> v{1, 2, 3, 4};
auto&& x = reverse(v);
The compiler created instance of reverse in this case would look like:
template<>
auto reverse<std::vector<int>&>(std::vector<int>& ob)
{
return revert_wrapper<std::vector<int>&>{ std::forward<std::vector<int>&>(ob) };
}
We see two things here:
The T of revert_wrapper will be std::vector<int>&, so no copy involved.
we're forwarding an lvalue as an lvalue to the constructor of revert_wrapper
1.2 Passing an rvalue std::vector<int>
std::vector<int> foo();
auto&& x = reverse(foo());
We look again at the instantiation of the function template:
template<>
auto reverse<std::vector<int>>(std::vector<int>&& ob)
{
return revert_wrapper<std::vector<int>>{ std::forward<std::vector<int>>(ob) };
}
And can again note two things:
The T of revert_wrapper will be std::vector<int>, thus copy the vector, preventing the rvalue from going out of scope before any range based loop can run
an rvalue std::vector<int>&& will be forwarded to the constructor of revert_wrapper
2. The class template revert_wrapper and its constructor
2.1 The revert_wrapper created by reverse in case of an lvalue std::vector<int>&
template<>
struct revert_wrapper<std::vector<int>&>
{
std::vector<int>& o;
revert_wrapper(std::vector<int>& i) :
o(std::forward<std::vector<int>&>(i)) {}
};
As noted above: No copies involved as we store a reference.
The forward also seems familiar and indeed it is just the same as above within reverse: We forward an lvalue as lvalue reference.
2.2 The revert_wrapper created by reverse in case of an rvalue std::vector<int>&&
template<>
struct revert_wrapper<std::vector<int>>
{
std::vector<int> o;
revert_wrapper(std::vector<int>&& i) :
o(std::forward<std::vector<int>>(i)) {}
};
This time we have the object stored by value to prevent a dangling reference.
Also the forwarding is fine: We forwarded the rvalue reference from reverse to the revert_wrapper constructor and we forward it on to the std::vector constructor. We could've used static_cast<T&&>(i) in the same way but we're not (std::)mov(e)ing from an lvalue, we're forwarding:
lvalues as lvalues and
rvalues as rvalues.
We can also see one more thing here:
The only available constructor of the revert_wrapper instance that stores by value takes an rvalue. Therefore, we can't (easily) trick this class to make unnecessary copies.
Note that replacing std::forward with std::move inside the initializer of o in the revert_wrapper constructor would actually be wrong.
Please see an excellent answer from TemplateRex here. I was able to solve the problem without a wrapper class, so I'll give a shot at answering my own question.
Here is the most helpful example I found on implementing iterators at http://en.cppreference.com, and you can find my updated ResizingArrayStack code at the same GitHub link as found the question.
template<typename T> class ResizingArrayStack {
public:
//----- Begin reversed iteration section -----//
// Please see the example here, (http://en.cppreference.com/w/cpp/iterator/iterator).
// Member typedefs inherit from std::iterator.
class stackIterator: public std::iterator<
std::input_iterator_tag, // iterator_category
T, // value_type
T, // difference_type
const T*, // pointer
T // reference
>{
int index = 0;
T* it_ptr = nullptr;
public:
// Prefix ++, equal, unequal, and dereference operators are the minimum required for range based for-loops.
stackIterator(int _index = 0, T* _it_ptr = nullptr) { index = _index; it_ptr = _it_ptr; }
// Here is where we reverse the sequence.
stackIterator& operator++() { --index; return *this; }
bool operator==(stackIterator other) { return index == other.index; }
bool operator!=(stackIterator other) { return !( *this == other ); }
T operator*() { return it_ptr[index-1]; }
};
stackIterator begin() { return stackIterator(N, array_ptr); }
stackIterator end() {
N = 0; // 'Empty' the array.
max_size = 1; // Don't waste time calling resize() now.
return stackIterator(0, array_ptr);
}
//----- End reversed iteration section -----//
private:
// Allocate space for a traditional array on the heap.
T* array_ptr = new T[1];
// Keep track of the space allocated for the array, max_size * sizeof(T).
int max_size = 1;
// Keep track of the current number of items on the stack.
int N = 0;
Calling code where the range based for-loop iterates in reversed (or LIFO) order by default.
// It's nice performance-wise to iterate in reverse without calling pop() or triggering a resize.
for ( auto i : lifo_stack) {
cout << "Current loop iteration has i = " << i << endl;
}

Extending iterators in C++

I'm trying to implement an iterator which encapsulates another iterator and performs range checking. Therefore I'm extending from that Iterator like so:
template<typename ITERATOR_T>
class r_iterator : public ITERATOR_T {
//...
r_iterator (ITERATOR_T begin, ITERATOR_T end) {
this->begin = begin;
this->end = end;
}
};
I want to use the iterators that are passed in the constructor to perform the range checking. My idea is to set the thing to which the "this"-iterator (i.e. r_iterator) points to to the element which is pointed to by the "begin" iterator from the constructor. This I would like to do so I can override certain methods like operator++ for example, perform range checking and then call the super method of the class I'm extending.
I would thus like to know whether it is possible to somehow set the element the "this"-iterator (r_iterator) points to assuming that I'm extending some STL Iterator class.
I could unfortunately not find any information about that in the c++ reference.
Regards and Thank you
There's no need to inherit, you can write a simple wrapper class like this:
#include <iostream>
#include <vector>
#include <stdexcept>
template <typename Iterator>
class MyIterator
{
Iterator _begin;
Iterator _end;
Iterator _cur;
public:
explicit MyIterator(Iterator begin, Iterator end)
: _begin(begin), _end(end)
{
_cur = _begin;
}
bool has_next() const
{
return (_cur != _end);
}
void operator++(int dummy)
{
if (!has_next())
throw std::out_of_range("Out of range.");
_cur++;
}
};
int main(int argc, char* argv[])
{
std::vector<int> v = {1, 2, 3};
MyIterator<std::vector<int>::iterator> my_it(v.begin(), v.end());
my_it++;
my_it++;
my_it++;
my_it++;
return 0;
}

pointer to vector at index Vs iterator

I have a vector< Object > myvec which I use in my code to hold a list of objects in memory. I keep a pointer to the current object in that vector in the "normal" C fashion by using
Object* pObj = &myvec[index];
This all works fine if... myvec doesn't grow big enough that it is moved around during a push_back at which time pObj becomes invalid - vectors guarantee data is sequential, hence they make no effort to keep the vector at the same memory location.
I can reserve enough space for myvec to prevent this, but I dnt' like that solution.
I could keep the index of the selected myvec position and when I need to use it just access it directly, but it's a costly modification to my code.
I'm wondering if iterators keep the their references intact as a vector is reallocated/moved and if so can I just replace
Object* pObj = &myvec[index];
by something like
vector<Object>::iterator = myvec.begin()+index;
What are the implication of this?
Is this doable?
What is the standard pattern to save pointers to vector positions?
Cheers
No... using an iterator you would have the same exact problem. If a vector reallocation is performed then all iterators are invalidated and using them is Undefined Behavior.
The only solution that is reallocation-resistant with an std::vector is using the integer index.
Using for example std::list things are different, but also the are different efficiency compromises, so it really depends on what you need to do.
Another option would be to create your own "smart index" class, that stores a reference to the vector and the index. This way you could keep just passing around one "pointer" (and you could implement pointer semantic for it) but the code wouldn't suffer from reallocation risks.
Iterators are (potentially) invalidated by anything that could resize the vector (e.g., push_back).
You could, however, create your own iterator class that stored the vector and an index, which would be stable across operations that resized the vector:
#include <iterator>
#include <algorithm>
#include <iostream>
#include <vector>
namespace stable {
template <class T, class Dist=ptrdiff_t, class Ptr = T*, class Ref = T&>
class iterator : public std::iterator<std::random_access_iterator_tag, T, Dist, Ptr, Ref>
{
T &container_;
size_t index_;
public:
iterator(T &container, size_t index) : container_(container), index_(index) {}
iterator operator++() { ++index_; return *this; }
iterator operator++(int) { iterator temp(*this); ++index_; return temp; }
iterator operator--() { --index_; return *this; }
iterator operator--(int) { stable_itertor temp(*this); --index_; return temp; }
iterator operator+(Dist offset) { return iterator(container_, index_ + offset); }
iterator operator-(Dist offset) { return iterator(container_, index_ - offset); }
bool operator!=(iterator const &other) const { return index_ != other.index_; }
bool operator==(iterator const &other) const { return index_ == other.index_; }
bool operator<(iterator const &other) const { return index_ < other.index_; }
bool operator>(iterator const &other) const { return index_ > other.index_; }
typename T::value_type &operator *() { return container_[index_]; }
typename T::value_type &operator[](size_t index) { return container_[index_ + index]; }
};
template <class T>
iterator<T> begin(T &container) { return iterator<T>(container, 0); }
template <class T>
iterator<T> end(T &container) { return iterator<T>(container, container.size()); }
}
#ifdef TEST
int main() {
std::vector<int> data;
// add some data to the container:
for (int i=0; i<10; i++)
data.push_back(i);
// get iterators to the beginning/end:
stable::iterator<std::vector<int> > b = stable::begin(data);
stable::iterator<std::vector<int> > e = stable::end(data);
// add enough more data that the container will (probably) be resized:
for (int i=10; i<10000; i++)
data.push_back(i);
// Use the previously-obtained iterators:
std::copy(b, e, std::ostream_iterator<int>(std::cout, "\n"));
// These iterators also support most pointer-like operations:
std::cout << *(b+125) << "\n";
std::cout << b[150] << "\n";
return 0;
}
#endif
Since we can't embed this as a nested class inside of the container like a normal iterator class, this requires a slightly different syntax to declare/define an object of this type; instead of the usual std::vector<int>::iterator whatever;, we have to use stable::iterator<std::vector<int> > whatever;. Likewise, to obtain the beginning of a container, we use stable::begin(container).
There is one point that may be a bit surprising (at least at first): when you obtain a stable::end(container), that gets you the end of the container at that time. As shown in the test code above, if you later add more items to the container, the iterator your obtained previously is not adjusted to reflect the new end of the container -- it retains the position it had when you obtained it (i.e., the position that was the end of the container at that time, but isn't any more).
No, iterators are invalidated after vector growth.
The way to get around this problem is to keep the index to the item, not a pointer or iterator to it. This is because the item stays at its index, even if the vector grows, assuming of course that you don't insert any items before it (thus changing its index).