This is a simplified instance of actual code. The goal was to provide access to container elements via range-based loops BUT have the elements changed on-the-fly (here ints are squared) without exposing the change.
The code works but seems overly complicated and unreadable. What simplification did I miss?
#include <vector>
#include <iostream>
using ivec_t = std::vector<int>;
class A {
ivec_t vec;
public:
A(const ivec_t& v) : vec(v) {}
class sq_class {
class iter {
ivec_t::iterator ivit;
public:
iter(ivec_t::iterator the_ivit) : ivit(the_ivit) {}
bool operator!=(const iter& other) { return ivit != other.ivit; }
iter& operator++() { ++ivit; return *this; }
int operator*() { return (*ivit)*(*ivit); } // <-------------------
};
ivec_t ivec;
public:
sq_class(ivec_t& the_ivec) : ivec(the_ivec) {}
iter begin() { return iter(ivec.begin()); }
iter end() { return iter(ivec.end()); }
};
sq_class squares() { return sq_class(vec); }
};
main()
{
A a({1,2,4});
// expected output: 1, 4, 16,
for (auto item : a.squares())
std::cerr << item << ", ";
std::cerr << "\n";
}
Related
Summary
I have a custom array class:
template<typename T, int SIZE>
class Array {
private:
T mArray[SIZE];
};
To enable support for std algorithms, with ranges, I want to create an iterator for this class. It would seem that std::contiguous_iterator would be the optimal choice since I can guarantee contiguous memory layout for the data. Following the iterator tutorial I should create a class inside this class. However, I should somehow be (quoted) "For example, instead of the std::forward_iterator_tag tag you would mark your iterator with the std::forward_iterator concept.".
I have a hard time figuring out what the syntax would look like for this, and I have been unable to find a post on the web showcasing this.
Question
How do I complete the following code snippet to implement std::contiguous_iterator for my Array<T,S> class?:
import <iterator>;
template<typename T, int SIZE>
class Array {
public:
const T& operator[](int i) { return mArray[i]; }
T& operator[](int i) { return mArray[i]; }
private:
T mArray[SIZE];
public:
struct Iterator {
Iterator(T* ptr) : mPtr(ptr) {}
private:
T* mPtr;
};
Iterator begin() { return Iterator(&mArray[0]); }
Iterator end() { return Iterator(&mArray[SIZE]); }
};
NOTE: There is a lot of operator overloads. An answer is not required to provide all of them. I just need an example syntax for where to place the concept, then I can probably figure out the rest.
As far as I could tell you need typedefs on the iterator class, so simply using a pointer was not sufficient. Here is an example:
#include <iterator>
#include <algorithm>
template<typename T, int SIZE>
class Array {
public:
const T& operator[](int i) const { return mArray[i]; }
T& operator[](int i) { return mArray[i]; }
private:
T mArray[SIZE];
public:
struct iterator
{
using difference_type=std::ptrdiff_t;
using value_type=std::remove_cv_t<T>;
using pointer=T*;
using reference=T&;
using iterator_category=std::random_access_iterator_tag;
using iterator_concept=std::contiguous_iterator_tag;
using self_type=iterator;
iterator(T *x) : ptr(x) {}
T operator*() { return *ptr; }
T operator->() { return ptr; }
difference_type operator-(const iterator& rhs) { return ptr-rhs.ptr; }
iterator& operator ++() { ++ptr; return *this;}
bool operator !=(const iterator& rhs) { return ptr != rhs.ptr; }
private:
T * ptr;
};
iterator begin() { return &mArray[0]; }
iterator end() { return &mArray[SIZE]; }
};
int foo(Array<int, 7>& a)
{
int sum;
for (auto x : a)
{
sum+=x;
}
return sum;
}
int goo(Array<int, 7>& a, int x)
{
auto ret=std::find(a.begin(), a.end(), x);
if (ret!=a.end()) return *ret;
return 0;
}
Note that you would likely need const_iterator and reverse_iterators for const and non-const ...
Credits
Thanks to #glenn-teitelbaum for pointing me in the right direction. I think I managed to figure out how to do this. It took a long time, so this will hopefully save someone else that trouble.
[Answering my own question]
The iterator should comply with the std::contiguous_iterator concept, so I looked at cppreference for the necessary parts. The concept is defined like this:
template<class I>
concept contiguous_iterator =
std::random_access_iterator<I> &&
std::derived_from</*ITER_CONCEPT*/<I>, std::contiguous_iterator_tag> &&
std::is_lvalue_reference_v<std::iter_reference_t<I>> &&
std::same_as<
std::iter_value_t<I>, std::remove_cvref_t<std::iter_reference_t<I>>
> &&
requires(const I& i) {
{ std::to_address(i) } ->
std::same_as<std::add_pointer_t<std::iter_reference_t<I>>>;
};
So in order to implement this concept, I must first also implement std::random_access_iterator, std::is_derived_from<[...]>, std::is_lvalue_reference_v<[...]>, and so on. This is a recursive process, especially since std::random_access_iterator builds on top of 4 other iterator types. Since this is a C++20 question, I aim to use C++20 features as much as possible (since it greatly simplifies the implementation). This is the complete iterator implementation:
template<typename T, int SIZE>
class Array
{
public:
class Iterator
{
public:
using iterator_category = std::contiguous_iterator_tag;
using iterator_concept = std::contiguous_iterator_tag;
//using difference_type = std::ptrdiff_t; // Likely the same
using difference_type = typename std::iterator<
std::contiguous_iterator_tag, T>::difference_type;
//using value_type = T;
using value_type = std::remove_cv_t<T>; // Using `T` seems sufficient
using pointer = T*;
using reference = T&;
// constructor for Array<T,S>::begin() and Array<T,S>::end()
Iterator(pointer ptr) : mPtr(ptr) {}
// std::weakly_incrementable<I>
Iterator& operator++() { ++mPtr; return *this; }
Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; }
Iterator() : mPtr(nullptr/*&mArray[0]*/) {} // TODO: Unsure which is correct!
// std::input_or_output_iterator<I>
reference operator*() { return *mPtr; }
// std::indirectly_readable<I>
friend reference operator*(const Iterator& it) { return *(it.mPtr); }
// std::input_iterator<I>
// No actions were needed here!
// std::forward_iterator<I>
// In C++20, 'operator==' implies 'operator!='
bool operator==(const Iterator& it) const { return mPtr == it.mPtr; }
// std::bidirectional_iterator<I>
Iterator& operator--() { --mPtr; return *this; }
Iterator operator--(int) { Iterator tmp = *this; --(*this); return tmp; }
// std::random_access_iterator<I>
// std::totally_ordered<I>
std::weak_ordering operator<=>(const Iterator& it) const {
return std::compare_three_way{}(mPtr, it.mPtr);
// alternatively: `return mPtr <=> it.mPtr;`
}
// std::sized_sentinel_for<I, I>
difference_type operator-(const Iterator& it) const { return mPtr - it.mPtr; }
// std::iter_difference<I> operators
Iterator& operator+=(difference_type diff) { mPtr += diff; return *this; }
Iterator& operator-=(difference_type diff) { mPtr -= diff; return *this; }
Iterator operator+(difference_type diff) const { return Iterator(mPtr + diff); }
Iterator operator-(difference_type diff) const { return Iterator(mPtr - diff); }
friend Iterator operator+(difference_type diff, const Iterator& it) {
return it + diff;
}
friend Iterator operator-(difference_type diff, const Iterator& it) {
return it - diff;
}
reference operator[](difference_type diff) const { return mPtr[diff]; }
// std::contiguous_iterator<I>
pointer operator->() const { return mPtr; }
using element_type = T;
private:
T* mPtr;
};
// === STATIC ASSERTS ===
// - to verify correct Iterator implementation!
static_assert(std::weakly_incrementable<Iterator>);
static_assert(std::input_or_output_iterator<Iterator>);
static_assert(std::indirectly_readable<Iterator>);
static_assert(std::input_iterator<Iterator>);
static_assert(std::incrementable<Iterator>);
static_assert(std::forward_iterator<Iterator>);
static_assert(std::bidirectional_iterator<Iterator>);
static_assert(std::totally_ordered<Iterator>);
static_assert(std::sized_sentinel_for<Iterator, Iterator>);
static_assert(std::random_access_iterator<Iterator>);
static_assert(std::is_lvalue_reference_v<std::iter_reference_t<Iterator>>);
static_assert(std::same_as<std::iter_value_t<Iterator>,
std::remove_cvref_t<std::iter_reference_t<Iterator>>>);
static_assert(std::contiguous_iterator<Iterator>);
const T& operator[](int i) const {
if (i < 0 || i >= SIZE) {
throw std::runtime_error("Array index out of bounds");
}
return mArray[i];
}
T& operator[](int i) {
if (i < 0 || i >= SIZE) {
throw std::runtime_error("Array index out of bounds");
}
return mArray[i];
}
Iterator begin() { return Iterator(&mArray[0]); }
Iterator end() { return Iterator(&mArray[SIZE]); }
private:
T mArray[SIZE];
};
// Check that the Array class can be used as a contiguous_range.
static_assert(std::ranges::contiguous_range<Array<int, 10>>);
NOTE: using element_type = T; was necessary because of a bug in the specification, which might be fixed. I found information about that here. Adding this fixed issue with std::to_address<Iterator> not being able to compile, and was the last missing piece in going from std::random_access_iterator to std::contiguous_iterator.
Testing
I did not perform a complete testing suite with all algorithms, but I chose a few which depend on ranges and std::random_access_iterator. It all runs smoothly. I also depend on building standard library headers as module units, because I want to showcase how C++20 features work together.
import <stdexcept>;
import <iostream>;
import <iterator>;
import <algorithm>;
import <random>;
#include <memory> // fails to build header unit!
template<typename T, int SIZE>
class Array
{
[...]
};
int main()
{
Array<int, 10> arr;
for (int i = 0; i < 10; i++) arr[i] = i;
// I need to call std::ragnes::shuffle since that depends on
// std::random_access_iterator, so that is a minimum.
// https://en.cppreference.com/w/cpp/algorithm/ranges/shuffle
std::random_device rd;
std::mt19937 gen{rd()};
std::cout << "before random shuffle:\n";
for (auto& i : arr) std::cout << i << ' ';
std::ranges::shuffle(arr, gen);
std::cout << "\nafter random shuffle:\n";
for (auto& i : arr) std::cout << i << ' ';
std::cout << '\n';
// Also std::ranges::stable_sort is a good check (also random_access_iterator):
// https://en.cppreference.com/w/cpp/algorithm/ranges/stable_sort
std::cout << "after stable_sort:\n";
std::ranges::stable_sort(arr);
for (auto& i : arr) std::cout << i << ' ';
std::cout << '\n';
auto [min,max] = std::ranges::minmax(arr);
std::cout << "min: " << min << ", max: " << max << '\n';
return 0;
}
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
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.
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;
}
From this old question
C++ custom collection reverse_iterator with similar behaviour to std::vector implementation
I thought that the lines (after revising his design a bit)
template <typename Iterator>
class reverse_iterator {
Iterator _it;
public:
reverse_iterator(const Iterator& it):_it(it) { }
Iterator base() const {Iterator it = _it; return --it;}
typename Iterator::value_type& operator*() const {return *base();}
// ...
};
were correct (no one replied that they were incorrect). But it doesn't give the same output that this test with std::reverse_iterator gives:
#include <iostream>
#include <iterator>
#include <vector>
int main () {
std::vector<int> myvector = {1,2,3};
std::vector<int>::iterator it = std::next(myvector.begin());
std::reverse_iterator<std::vector<int>::iterator> r(it);
std::cout << *it << '\n'; // 2
std::cout << *r << '\n'; // 1
std::cout << *r.base() << '\n'; // 2
}
which seems to show that the lines should instead be
template <typename Iterator>
class reverse_iterator {
Iterator _it;
public:
reverse_iterator(const Iterator& it):_it(it) { }
Iterator base() const { return _it; }
typename Iterator::value_type& operator*() const { return *--base(); }
// ...
};
which would indeed give the same output as with std::reverse_iterator. What is correct here?
I want to create a custom map that that actually uses a fixed set of keys, but should behave like a std::map. Basically I use an array internally and map the keys to indexes, allowing very efficient lookup. I am however struggling to implement iterators that behave like std::map iterators, because I do not have internal std::pairs that I can hand out references to.
Is it possible to implement this as a zero-overhead abstraction while retaining the std::map interface, in particular the iterators?
The best i could come up with as operator* is to return a rvalue std::pair<key_type, mapped_type*>, which basically allows for the same operations, but unfortunately with different usage patterns.
I have also tried std::pair<key_type, boost::referene_wrapper<mapped_type>>, but that still doesn't allow for(auto& elem : map) and often requires elem.second.get() for reasons I do not understand.
I am happy to use boost or lightweight header libraries, if there is anything that helps for the use case.
To illustrate the case, here is a minimal example with a map that contains all letters 'a'-'z'.
using letter = char;
static const letter letter_begin = 'a';
static const letter letter_end = 'z' + 1;
template <typename T>
class letter_map
{
private:
using self = letter_map<T>;
template <typename IT, typename M>
class iterator_base : public std::iterator<std::input_iterator_tag, T>
{
public:
iterator_base(letter index, M& map) : index_(index), data_(map)
{
}
using self_iter = iterator_base<IT, M>;
IT operator*()
{
return IT(index_, &data_[index_]);
}
self_iter& operator++()
{
index_++;
return *this;
}
self_iter operator++(int)
{
self_iter tmp(*this);
operator++();
return tmp;
}
bool operator==(self_iter other) const
{
assert(&data_ == &other.data_);
return index_ == other.index_;
}
bool operator!=(self_iter other) const
{
return !(*this == other);
}
private:
letter index_;
M& data_;
};
public:
using key_type = letter;
using mapped_type = T;
using value_type = std::pair<const key_type, mapped_type*>;
using const_value_type = std::pair<const key_type, const mapped_type*>;
private:
static const size_t data_size = letter_end - letter_begin;
using container_type = std::array<mapped_type, data_size>;
public:
using iterator = iterator_base<value_type, self>;
using const_iterator = iterator_base<const_value_type, const self>;
public:
mapped_type& operator[](letter l)
{
return data_[l - letter_begin];
}
const mapped_type& operator[](letter l) const
{
return data_[l - letter_begin];
}
auto begin()
{
return iterator(letter_begin, *this);
}
auto end()
{
return iterator(letter_end, *this);
}
auto begin() const
{
return const_iterator(letter_begin, *this);
}
auto end() const
{
return const_iterator(letter_end, *this);
}
private:
container_type data_;
};
void print_string_letter_map(const letter_map<std::string>& lm)
{
for (auto elem : lm)
{
std::cout << elem.first << "->" << *(elem.second) << std::endl;
}
}
template<typename T>
class std_letter_map : public std::map<letter, T>
{
public:
std_letter_map()
{
for (letter l = letter_begin; l != letter_end; ++l) {
this->emplace(l, T());
}
}
};
void print_string_std_letter_map(const std_letter_map<std::string>& lm)
{
for (const auto& elem : lm)
{
std::cout << elem.first << "->" << elem.second << std::endl;
}
}
int main()
{
letter_map<std::string> lm;
// usually I would use auto& elem here
for (auto elem : lm) {
auto let = elem.first;
// usually this would be without the *
auto& str = *(elem.second);
str = std::string("foo ") + let;
}
print_string_letter_map(lm);
return 0;
}
Implementing operator * is easy - just return std::pair<const Key, Value&> or ..., Value const&> for const iterator, like in this simplified example:
template <typename T>
class iterator_array_as_map
{
public:
iterator_array_as_map(T* array, int index)
: array(array), index(index)
{}
bool operator == (const iterator_array_as_map& other) const
{
return index == other.index;
}
bool operator != (const iterator_array_as_map& other) const
{
return index != other.index;
}
iterator_array_as_map& operator ++ ()
{
++index;
return *this;
}
auto operator * ()
{
return std::pair<const int, T&>(index, array[index]);
}
private:
T* array;
int index;
};
And usage:
int main() {
int array[2] = {2, 4};
auto begin = iterator_array_as_map<int>(array, 0);
auto end = iterator_array_as_map<int>(array, 2);
for (auto it = begin; it != end; ++it)
{
std::cout << (*it).first << " " << (*it).second << std::endl;
}
(*begin).second = 7;
for (auto it = begin; it != end; ++it)
{
std::cout << (*it).first << " " << (*it).second << std::endl;
}
}
operator -> is a little harder - since you must return something which needs to emulate pointer to std::pair - but if do not mind about dynamic memory fragmentation - you can just return std::shared_ptr<std::pair<....>>...
auto operator -> ()
{
return std::make_shared<std::pair<const int, T&>>(index, array[index]);
}
If you do not want to use dynamic memory - then you might try with pointer to itself solution:
template<typename data>
class pointer_to_data
{
public:
template<typename ...Arg>
pointer_to_data(Arg&&... arg)
: data{std::forward<Arg>(arg)...}
{}
Data* operator -> ()
{
return &data;
}
private:
Data data;
};
Just return the above instead shared_ptr...
See this what-is-the-correct-way-of-using-c11s-range-based-for, section "The special case of proxy iterators". It is not possible to define for(auto&e:b) if b is e.g. std::vector<bool>- because in general it is not possible to have reference to temporary, and this very container is similar to yours in this sense that it has "Special" reference type.
You can try to have special member in your iterator keeping the "return value"- but that would be troublesome - so probably no better that this presented by me solution exist.
You could probably use zip_view from Eric Niebler's range library
https://github.com/ericniebler/range-v3
I have two iterators, both derive from boost::iterator_facade (but not from each other) and I want to be able to compare them because I don't want to have more end() methods when one is sufficient. Is it possible?
The following minimal example is not working for me, but I think it should. What am I doing wrong?
#include <vector>
#include <iostream>
#include <boost/iterator/iterator_facade.hpp>
using namespace std;
typedef vector<int> val_type;
typedef vector<val_type> vec_type;
class myiterator
: public boost::iterator_facade<
myiterator
, val_type
, boost::forward_traversal_tag
>
{
private:
friend class boost::iterator_core_access;
friend class base_myiterator;
public:
explicit myiterator(vec_type::iterator _it)
: it(_it)
{}
myiterator(myiterator const& other)
: it(other.it)
{}
private:
void increment() { ++it; }
bool equal(myiterator const& other) const
{
return (it == other.it);
}
val_type& dereference() const { return *it; }
vec_type::iterator it;
};
class base_myiterator
: public boost::iterator_facade<
base_myiterator
, val_type
, boost::forward_traversal_tag
>
{
private:
friend class boost::iterator_core_access;
public:
explicit base_myiterator(vec_type::const_iterator _it, val_type _base)
: base(_base),
it(_it)
{
idx.resize(base.size());
}
base_myiterator(base_myiterator const& other)
: base(other.base),
it(other.it)
{
idx.resize(base.size());
}
private:
void increment()
{
++it;
for (size_t i=0; i<base.size(); ++i)
{
idx[i] = base[i] + (*it)[i];
}
}
bool equal(base_myiterator const& other) const
{
return (it == other.it);
}
bool equal(myiterator const& other) const
{
return (it == other.it);
}
val_type const& dereference() const
{
return idx;
}
val_type base;
vec_type::const_iterator it;
val_type idx;
};
struct Sample
{
vec_type vec;
myiterator begin()
{
return myiterator(vec.begin());
}
base_myiterator begin(val_type const& base)
{
return base_myiterator(vec.begin(), base);
}
myiterator end()
{
return myiterator(vec.end());
}
};
int main ()
{
Sample s;
val_type val;
val.push_back(1); val.push_back(0);
s.vec.push_back(val);
val.clear(); val.push_back(0); val.push_back(1);
s.vec.push_back(val);
val.clear(); val.push_back(-5); val.push_back(5);
//for (myiterator it=s.begin(); it!=s.end(); ++it)
for (base_myiterator it=s.begin(val); it!=s.end(); ++it)
{
cout << (*it)[0] << " " << (*it)[1] << endl;
}
}
boost::iterator_facade checks if two iterators are interoperable before instantiating the relation operators.
It uses boost::type_traits::is_convertible to check if it is legal to convert one type of iterator into the other type. Thus if you add a constructor base_myiterator(myiterator const& other) it will work. It will use the special equal overload you provided and not do any conversion (i.e. the additional copy constructor won't be used, it just needs to be there).
Furthermore, the base_myiterator seems to operate as some sort of const_iterator (val_type const& reference() const). In this case you should pass val_type const as template parameter Value to iterator_facade as described here.