Reputation: 3
I would like to find a clean way to slightly change the way std::vector operates.
Problem background
I need to be able to have index in the vector where the pointer would essentially slip one back, so for example; if my vector contains {0,1,2,3,4,5} and index [3] needs to slip, when iterating through the vector it should return: 0, 1, 2, 3, 3, 4, 5
Problem
Without rewriting the entire vector class and implementing my changes, is it possible to inherit std::vector<T>
into a custom class, and override the behavior of the iterators ++ operator as well as the vector::begin()? (to add memory of the fact that it has already slipped one and not to slip again)
What I have tried
I have tried a basically implementation so far, but have gotten stuck when trying to override the begin() function.
#include <vector>
#include <iostream>
#include <iterator>
template <typename T>
class myVector : public std::vector<T>{
public:
class myIterator : public std::vector<T>::iterator
{
// call vector's iterotor constructor
//override
// iterator& operator++(void){}
// iterator operator++(int){}
};
using std::vector<T>::vector; // use the constructor from vector
// extend the begin function, but include original operation
myVector::myIterator begin() const
{
std::cout << "hi\n"; // testing to see if begin has been overriden properly
return std::vector<T>::begin();
}
};
// print out the vector
template <typename T>
std::ostream& operator<<(std::ostream& os, const myVector<T>& v)
{
auto begin=v.begin();
while (begin!=v.end())
{
os << *begin;
++begin;
if(begin!=v.end())
{
os << ',';
}
}
return os;
}
int main() {
myVector<int> vec = {1,2,3,4};
std::cout << vec << '\n';
}
If you have an alternative clean solutions, it is also welcome.
Thanks
Upvotes: 0
Views: 629
Reputation: 2004
As others already said, just define a custom iterator. If you moreover want the nice range iteration, you can then just define a custom range class returning your custom iterators.
See a working example below (needs --std=c++17):
#include <vector>
#include <iostream>
template<typename T>
class SlipIterator {
private:
using iterator = typename std::vector<T>::const_iterator;
iterator i;
iterator slip;
bool has_slipped;
public:
SlipIterator(iterator i, iterator slip): i(i), slip(slip), has_slipped(false) {}
SlipIterator &operator++() {
if ((!has_slipped) && (i == slip))
has_slipped = true;
else
++i;
return *this;
}
bool operator!=(SlipIterator<T> &the_end) const { return i != the_end.i; }
const T &operator*() const { return *i; }
};
template<typename T>
class SlipperyRange {
private:
const std::vector<T> &v;
size_t slip_index;
public:
SlipperyRange(const std::vector<T> &v, size_t slip_index) : v(v), slip_index(slip_index) {}
SlipIterator<T> begin() const { return SlipIterator<T>(v.cbegin(), v.cbegin() + slip_index); }
SlipIterator<T> end() const { return SlipIterator<T>(v.cend(), v.cend()); }
};
int main() {
std::vector<int> v{1,2,3,4,5};
for(const int i: SlipperyRange{v, 2})
std::cout << i << ' ';
std::cout << '\n';
return 0;
}
Upvotes: 1
Reputation: 122994
I would never inherit from std::vector
publicly. It is just too easy to be used wrong and you have no control over misuse. std::vector
has no virtual destructor!
Iterators seems to be the appropriate customization point. Much more is needed to get a fully compliant iterator, but this is enough for a working example:
#include <iterator>
#include <vector>
#include <iostream>
template <typename It>
struct slippery_iterator {
bool slipped = false;
It to_be_repeated;
It iterator;
slippery_iterator(It iterator,It to_be_repeated) : iterator(iterator),to_be_repeated(to_be_repeated){}
slippery_iterator& operator++(){
if (!slipped && iterator == to_be_repeated){
slipped = true;
return *this;
}
++iterator;
return *this;
}
typename std::iterator_traits<It>::reference operator*(){
return *iterator;
}
bool operator!=(It other) { return iterator != other;}
};
template <typename It>
slippery_iterator<It> make_slippery_iterator(It it,It slip){
return {it,slip};
}
int main() {
std::vector<int> x{1,2,3,4,5};
auto begin = make_slippery_iterator(x.begin(),x.begin()+2);
for (; begin != x.end(); ++begin){
std::cout << *begin;
}
}
Output:
123345
PS: Note that I am used to C++11 where you need a make_x
helper. It isnt needed with more recent standards.
Upvotes: 4