I'm trying this little exercice from my c++ class and i've manage to do this from now.
It's not fully completed, but for now i'm facing the eternal problem of template of internal class.
I've seen a lot of different solution here on stack and on other websites, but still missing someting.
+ my goal at the end is also to understand the "why".
It's an Iterator internal class needed to iterate to my array. I've seen some exemple giving 2 differents typename for External and Iternal class and then using the typedef, but i'm not sure what's the best way to implement it.
As you can understand my Iterator class need to take the same kind of type as my Array class.
I for sure will need to change some function signatures and add some <\T> here and there, but for now i just need to avoid all the template traps.
using namespace std;
template <typename T>
class Iterator;
template<typename T>
class Array{
T* _data;
size_t _size;
Iterator* _start;
public:
class Iterator{
T* content;
public:
explicit Iterator(T* value):content(value){}
Iterator& operator++(){
++content;
return *this;
}
T* operator*(){
return content;
}
bool operator ==(const Iterator& o)const{
if(content == o.content){
return true;
}
return false;
}
bool operator !=(const Iterator& o)const{
return !(*this == o);
}
};
Array(const size_t s):_size(s){
_data = new T[_size]();
_start = new Iterator(_data);
}
Array(const Array& o):_size(o._size), _data(o._data), _start(o._start){}
Array(const std::initializer_list<T>& list):_size(list.size()){
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while(start != fin){
_data[index++] = *start++;
}
_start = new Iterator(_data);
}
virtual ~Array(){
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o){
if(this != &o){
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index){
if(index < _size){
return *_data[index];
}
}
const size_t size()const{
return _size;
}
Iterator begin(){
return Iterator(_data[0]);
}
Iterator end(){
return Iterator(_data[_size-1]);
}
};
Can you please give me a clue or help me with this.
For more here is my basic main:
#include "Array.h"
int main() {
Array<string> array({"h","e","l","l","o"});
for (Array<string>::Iterator i = array.begin(); i != array.end(); ++i)
cout << *i << endl;
return 0;
}
Thank you!
There is no template Iterator at the global scope, so this is wrong:
template <typename T>
class Iterator;
Also, Array<T>::Iterator isn't a template, it's just an inner class. You can simply forward-declare it inside the class like this:
template<typename T>
class Array {
public:
class Iterator;
Then there are some bugs in your code (e.g. end() should be 1 past the last element, you need to dereference the iterator twice and construct one from a pointer).
Here's a fixed version:
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class Array {
T* _data;
size_t _size;
public:
class Iterator;
private:
Iterator* _start;
public:
class Iterator {
T* content;
public:
explicit Iterator(T* value) :content(value) {}
Iterator& operator++() {
++content;
return *this;
}
T* operator*() {
return content;
}
bool operator ==(const Iterator& o)const {
if (content == o.content) {
return true;
}
return false;
}
bool operator !=(const Iterator& o)const {
return !(*this == o);
}
};
Array(const size_t s) :_size(s) {
_data = new T[_size]();
_start = new Iterator(_data);
}
Array(const Array& o) :_size(o._size), _data(o._data), _start(o._start) {}
Array(const std::initializer_list<T>& list) :_size(list.size()) {
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while (start != fin) {
_data[index++] = *start++;
}
_start = new Iterator(_data);
}
virtual ~Array() {
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o) {
if (this != &o) {
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index) {
if (index < _size) {
return *_data[index];
}
}
const size_t size()const {
return _size;
}
Iterator begin() {
return _start;
}
Iterator end() {
return Iterator(_data + _size);
}
};
int main() {
Array<string> array({ "h","e","l","l","o" });
for (Array<string>::Iterator i = array.begin(); i != array.end(); ++i)
cout << **i << endl;
}
Thank you #rustyx for your help,
Wasn't far from it, but realy thank you.
I will post my corrected and working code here in case it can help others.
#include <cstdlib>
#include <string>
#include <iostream>
using namespace std;
template<typename T>
class Array{
T* _data;
size_t _size;
public:
class Iterator{
T* content;
public:
explicit Iterator(T* value):content(value){}
Iterator& operator++(){
++content;
return *this;
}
T& operator*(){
return *content;
}
bool operator ==(const Iterator& o)const{
if(content == o.content){
return true;
}
return false;
}
bool operator !=(const Iterator& o)const{
return !(*this == o);
}
};
Array(const size_t s):_size(s){
_data = new T[_size]();
}
Array(const Array& o):_size(o._size){
_data = new T[_size];
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
}
Array(const std::initializer_list<T>& list):_size(list.size()){
auto start = list.begin();
auto fin = list.end();
_data = new T[_size];
size_t index = 0;
while(start != fin){
_data[index++] = *start++;
}
}
virtual ~Array(){
cout << "~Array" << endl;
delete[] _data;
}
Array<T>& operator= (const Array& o){
if(this != &o){
delete[] _data;
for (size_t i = 0; i < o._size; ++i) {
_data[i] = o._data[i];
}
_size = o._size;
}
return *this;
}
T& operator[](const size_t index){
if(index < _size){
return *_data[index];
}
}
const size_t size()const{
return _size;
}
Iterator begin(){
return Iterator(_data);
}
Iterator end(){
return Iterator(_data + _size);
}
};
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 have a class template for a Circular buffer
#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H
#include <algorithm>
#include <cstddef>
#include <cassert>
#include <stdexcept>
#include <iostream>
template <typename T>
class CircularBuffer
{
public:
typedef size_t size_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
explicit CircularBuffer(size_type capacity);
CircularBuffer(const CircularBuffer<T> &rhs);
CircularBuffer(CircularBuffer<T>&& rhs);
~CircularBuffer() { if (_buffer) delete[] _buffer; }
CircularBuffer<T>& operator=(CircularBuffer<T> rhs);
size_type size() const { return (_full ? _capacity : _front); }
size_type capacity() const { return _capacity; }
bool is_full() const { return _full; }
const_reference operator[](size_type index) const;
reference operator[](size_type index);
void add(T item);
void resize(size_type new_capacity);
friend void swap(CircularBuffer<T> &a, CircularBuffer<T> &b)
{
std::swap(a._buffer, b._buffer);
std::swap(a._capacity, b._capacity);
std::swap(a._front, b._front);
std::swap(a._full, b._full);
}
private:
pointer _buffer;
size_type _capacity;
size_type _front;
bool _full;
CircularBuffer();
};
template<typename T>
CircularBuffer<T>::CircularBuffer()
: _buffer(nullptr)
, _capacity(0)
, _front(0)
, _full(false)
{
}
template<typename T>
CircularBuffer<T>::CircularBuffer(size_type capacity)
: CircularBuffer()
{
if (capacity < 1) throw std::length_error("Invalid capacity");
_buffer = new T[capacity];
_capacity = capacity;
}
template<typename T>
CircularBuffer<T>::CircularBuffer(const CircularBuffer<T> &rhs)
: _buffer(new T[rhs._capacity])
, _capacity(rhs._capacity)
, _front(rhs._front)
, _full(rhs._full)
{
std::copy(rhs._buffer, rhs._buffer + _capacity, _buffer);
}
template<typename T>
CircularBuffer<T>::CircularBuffer(CircularBuffer<T>&& rhs)
: CircularBuffer()
{
swap(*this, rhs);
}
template<typename T>
typename CircularBuffer<T>::const_reference
CircularBuffer<T>::operator[](size_type index) const
{
static const std::out_of_range ex("index out of range");
if (index < 0) throw ex;
if (_full)
{
if (index >= _capacity) throw ex;
return _buffer[(_front + index) % _capacity];
}
else
{
if (index >= _front) throw ex;
return _buffer[index];
}
}
template<typename T>
typename CircularBuffer<T>::reference
CircularBuffer<T>::operator[](size_type index)
{
return const_cast<reference>(static_cast<const CircularBuffer<T>&>(*this)[index]);
}
template<typename T>
CircularBuffer<T>&
CircularBuffer<T>::operator=(CircularBuffer<T> rhs)
{
swap(*this, rhs);
return *this;
}
template<typename T>
void
CircularBuffer<T>::add(T item)
{
_buffer[_front++] = item;
if (_front == _capacity) {
_front = 0;
_full = true;
}
}
template<typename T>
void
CircularBuffer<T>::resize(size_type new_capacity)
{
if (new_capacity < 1) throw std::length_error("Invalid capacity");
if (new_capacity == _capacity) return;
size_type num_items = size();
size_type offset = 0;
if (num_items > new_capacity)
{
offset = num_items - new_capacity;
num_items = new_capacity;
}
pointer new_buffer = new T[new_capacity];
for (size_type item_no = 0; item_no < num_items; ++item_no)
{
new_buffer[item_no] = (*this)[item_no + offset];
}
pointer old_buffer = _buffer;
_buffer = new_buffer;
_capacity = new_capacity;
_front = (num_items % _capacity);
_full = (num_items == _capacity);
delete[] old_buffer;
}
#endif // CIRCULAR_BUFFER_H
Usually I initialize it like this
timed_temperature aTimedTemperature;
aTimedTemperature.dt =102019;
aTimedTemperature.temp=37.0;
CircularBuffer<timed_temperature> myCircularBufferedTemps = CircularBuffer<timed_temperature> (PLOT_RING_BUFFER_SIZE);
myCircularBufferedFilteredTemps.add(aTimedTemperature.dt);
So I tried to create a vector of CircularBuffer<timed_temperature> like this
std::vector<CircularBuffer<timed_temperature>> *myTempsVector = new std::vector< CircularBuffer<timed_temperature>(PLOT_RING_BUFFER_SIZE) >;
But obviously for all of you, it does not work!
I tried multiple things without success.
So my question is only how can I declare in my header a member which is a vector of my CircularBuffer<timed_temperature>(PLOT_RING_BUFFER_SIZE)
And how I access the myTempsVector[i].is_full() method of one element of the vector?
Thank you for your help.
You're confusing between CircularBuffer<timed_temperature>'s constructor and std::vector<T> constructor.
When you write:
std::vector< CircularBuffer<timed_temperature> >(PLOT_RING_BUFFER_SIZE)
It'll call std::vector's constructor with PLOT_RING_BUFFER_SIZE, trying to allocate PLOT_RING_BUFFER_SIZE different CircularBuffer<timed_temperature> using the default constructor.
Simply define your vector as such:
std::vector<CircularBuffer<timed_temperature>> myTempsVector;
Then add (push_back) any new instance you want, e.g. :
myTempsVector.push_back(CircularBuffer<timed_temperature> (PLOT_RING_BUFFER_SIZE));
You simply need to create a vector first...
std::vector<CircularBuffer<timed_temperature>> myTempsVector;
...and then put a value into it:
myTempsVector.push_back(CircularBuffer<timed_temperature>(PLOT_RING_BUFFER_SIZE));
While you're at it, you should not be performing manual memory management and storing a pointer unless you really need to. If you do need to store a vector through a pointer, consider using std::unique_ptr, or std::shared_ptr if std::unique_ptr doesn't work.
I've been working on a small-scale test to see if I can figure out some compiler-specific larger-scale problems with a larger container. The following code works fine in GCC but causes the following error code in Visual Studio 2010 and 2013:
"Error 1 error C2675: unary '--' : 'std::iterator' does not define this operator or a conversion to a type acceptable to the predefined operator d:\programming\workspaces\adl_test\main.cpp 127 1 adL_test_msvc"
Test code is as follows:
#include <iostream>
#include <iterator>
namespace nsp
{
template <class element_type, class element_allocator_type = std::allocator<element_type> >
class test_container
{
private:
element_type numbers[50];
friend class iterator;
friend class reverse_iterator;
public:
class reverse_iterator; //forward decl
class iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
element_type *i;
template <class distance_type>
friend void advance(reverse_iterator &rit, distance_type n);
public:
iterator() {}
iterator(element_type &_i)
{
i = &(_i);
}
element_type & operator *()
{
return *i;
}
iterator & operator = (const element_type &source_i)
{
i = &(source_i);
return *this;
}
iterator & operator = (const iterator &source)
{
i = source.i;
return *this;
}
bool operator != (const iterator rh)
{
return i != rh.i;
}
iterator & operator ++()
{
++i;
return *this;
}
iterator & operator --()
{
--i;
return *this;
}
template <class distance_type>
friend void advance(iterator &it, distance_type n)
{
it.i += n;
}
friend typename std::iterator_traits<iterator>::difference_type distance(const iterator &first, const iterator &last)
{
return last.i - first.i;
}
};
class reverse_iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
iterator it;
public:
reverse_iterator(element_type &_i)
{
it.i = _i;
}
reverse_iterator(const iterator &source)
{
it = source;
}
element_type & operator *()
{
return *it;
}
element_type & operator = (const reverse_iterator &source)
{
it = source.it;
return *this;
}
element_type & operator = (const iterator &source)
{
it = source;
return *this;
}
bool operator != (const iterator rh)
{
return it != rh.it;
}
reverse_iterator & operator ++()
{
--it;
return *this;
}
reverse_iterator & operator --()
{
++it;
return *this;
}
template <class distance_type>
friend void advance(reverse_iterator &rit, distance_type n)
{
rit.it.i -= n;
}
friend typename std::iterator_traits<reverse_iterator>::difference_type distance(const reverse_iterator &first, const reverse_iterator &last)
{
return distance(last.it, first.it);
}
};
iterator begin()
{
return iterator(numbers[0]);
}
iterator end()
{
return iterator(numbers[50]);
}
};
}
int main(int argc, char **argv)
{
nsp::test_container<int> stuff;
int counter = 0;
for (nsp::test_container<int>::iterator it = stuff.begin(); it != stuff.end(); ++it)
{
*it = counter++;
}
nsp::test_container<int>::iterator it = stuff.begin(), it2 = stuff.begin();
using namespace std;
std::cout << *it << std::endl;
++it;
--it;
++it;
std::cout << *it << std::endl;
advance(it, 2);
std::cout << *it << std::endl;
std::advance(it, 2);
std::cout << *it << std::endl;
int distance_between = distance(it2, it);
std::cout << distance_between << std::endl;
nsp::test_container<int>::reverse_iterator rit = it, rit2 = it2;
--rit;
++rit;
advance(rit, -2);
distance_between = distance(rit2, rit);
std::cout << distance_between << std::endl;
std::cin.get();
return 0;
}
Obviously the -- operator works fine on iterator as is demonstrated in the code, but when called from reverse_iterator MSVC creates an error, despite reverse_iterator being a friend. Why?
And what is the workaround for this bug?
Don't suggest different ways of approaching the code (ie. modifying i directly instead of calling -- operator), this is a test case, not actual working code. It does not represent the complexity of the actual code and I won't explain to you why the actual code works in this way because I don't have time.
Thanks to immibus, the answer has been found:
GCC believes 'iterator' within reverse_iterator refers to test_container::iterator, whereas (for some reason) MSVC 2010-2013 believes it refers to the base class.
The solution is to be more specific when specifying iterator within reverse_iterator - using "typename test_container::iterator" instead of "iterator".
Corrected code:
#include <iostream>
#include <iterator>
namespace nsp
{
template <class element_type, class element_allocator_type = std::allocator<element_type> >
class test_container
{
private:
element_type numbers[50];
friend class iterator;
friend class reverse_iterator;
public:
class reverse_iterator; //forward decl
class iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
element_type *i;
template <class distance_type>
friend void advance(reverse_iterator &rit, distance_type n);
public:
iterator() {}
iterator(element_type &_i)
{
i = &(_i);
}
element_type & operator *()
{
return *i;
}
iterator & operator = (const element_type &source_i)
{
i = &(source_i);
return *this;
}
iterator & operator = (const iterator &source)
{
i = source.i;
return *this;
}
bool operator != (const iterator rh)
{
return i != rh.i;
}
iterator & operator ++()
{
++i;
return *this;
}
iterator & operator --()
{
--i;
return *this;
}
template <class distance_type>
friend void advance(iterator &it, distance_type n)
{
it.i += n;
}
friend typename std::iterator_traits<iterator>::difference_type distance(const iterator &first, const iterator &last)
{
return last.i - first.i;
}
};
class reverse_iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
typename test_container::iterator it;
public:
reverse_iterator(element_type &_i)
{
it.i = _i;
}
reverse_iterator(const typename test_container::iterator &source)
{
it = source;
}
element_type & operator *()
{
return *it;
}
element_type & operator = (const reverse_iterator &source)
{
it = source.it;
return *this;
}
element_type & operator = (const typename test_container::iterator &source)
{
it = source;
return *this;
}
bool operator != (const typename test_container::iterator rh)
{
return it != rh.it;
}
reverse_iterator & operator ++()
{
--it;
return *this;
}
reverse_iterator & operator --()
{
++it;
return *this;
}
template <class distance_type>
friend void advance(reverse_iterator &rit, distance_type n)
{
rit.it.i -= n;
}
friend typename std::iterator_traits<reverse_iterator>::difference_type distance(const reverse_iterator &first, const reverse_iterator &last)
{
return distance(last.it, first.it);
}
};
iterator begin()
{
return iterator(numbers[0]);
}
iterator end()
{
return iterator(numbers[50]);
}
};
}
int main(int argc, char **argv)
{
nsp::test_container<int> stuff;
int counter = 0;
for (nsp::test_container<int>::iterator it = stuff.begin(); it != stuff.end(); ++it)
{
*it = counter++;
}
nsp::test_container<int>::iterator it = stuff.begin(), it2 = stuff.begin();
using namespace std;
std::cout << *it << std::endl;
++it;
--it;
++it;
std::cout << *it << std::endl;
advance(it, 2);
std::cout << *it << std::endl;
std::advance(it, 2);
std::cout << *it << std::endl;
int distance_between = distance(it2, it);
std::cout << distance_between << std::endl;
nsp::test_container<int>::reverse_iterator rit = it, rit2 = it2;
--rit;
++rit;
advance(rit, -2);
distance_between = distance(rit2, rit);
std::cout << distance_between << std::endl;
std::cin.get();
return 0;
}
I implemented a trivial class MyClass that has an array allocated with new inside it (I know that I could use a STL container, but I'm trying to understand how they work). I also created an iterator subclass, able to iterate on all the elements of a MyClass object:
class MyIterator : public iterator<forward_iterator_tag,int>
{
private:
int* data= nullptr;
int length;
int pointer=0;
int nullvalue=0;
public:
MyIterator(int* data, int length, bool end= false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
~MyIterator()
{
delete[] data;
}
MyIterator& operator++()
{
if(pointer!= length-1)
{
pointer++;
}
else
{
pointer= -1;
}
return *this;
}
bool operator==(const MyIterator& other)
{
return pointer==other.pointer;
}
bool operator!=(const MyIterator& other)
{
return pointer!= other.pointer;
}
int& operator* ()
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
};
class MyClass
{
private:
int* data= nullptr;
int length= 100;
public:
MyClass()
{
data= new int[length];
for(int i=0; i<length;i++)
data[i]=i+1;
}
iterator<forward_iterator_tag,int> begin()
{
return MyIterator(data,length);
}
iterator<forward_iterator_tag,int> end()
{
return MyIterator(data,length,true);
}
};
While the iterator works if I use it this way:
for(MyIterator i= MyClass_instance.begin(); i!=MyClass_instance.end();i++) {...}
It doesn't work if I try to use it with BOOST_FOREACH:
BOOST_FOREACH(int i, MyClass_instance) {...}
These are the errors that I get:
You're slicing your iterator by returning them as std::iterator<> by value. You cannot do that.
Returning by reference would avoid the slicing problem but introduces a worse problem: it returns a reference to a temporary¹.
Hence, fix it by returning your actual iterator type by value.
Your type was missing a const iterator.
All the iterator members weren't const-correct.
Also, according to the page Extensibility it looks like you need to add
namespace boost {
template<> struct range_mutable_iterator<MyClass> {
typedef MyClass::MyIterator type;
};
template<> struct range_const_iterator<MyClass> {
typedef MyClass::MyConstIterator type;
};
}
There is serious trouble with the Rule-Of-Three implementation for your iterator type (What is The Rule of Three?).
You're deleting the container's data every time an iterator goes out of existence. And MyClass itself never frees the data...
Fixing most (?) of the above:
Live On Coliru
#include <iterator>
#include <boost/foreach.hpp>
class MyClass
{
private:
int* data = nullptr;
int length = 100;
public:
class MyIterator : public std::iterator<std::forward_iterator_tag, int>
{
public:
MyIterator(int* data, int length, bool end = false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
MyIterator& operator++()
{
if(pointer!= length-1) {
pointer++;
}
else {
pointer= -1;
}
return *this;
}
bool operator==(const MyIterator& other) const { return pointer==other.pointer; }
bool operator!=(const MyIterator& other) const { return pointer!= other.pointer; }
int& operator*() const
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
private:
value_type* data = nullptr;
int length;
int pointer = 0;
mutable value_type nullvalue = 0;
};
class MyConstIterator : public std::iterator<std::forward_iterator_tag, const int>
{
public:
MyConstIterator(int const* data, int length, bool end = false)
{
this->data= data;
this->length= length;
if(end)
pointer=-1;
}
MyConstIterator& operator++()
{
if(pointer!= length-1) {
pointer++;
}
else {
pointer= -1;
}
return *this;
}
bool operator==(const MyConstIterator& other) const { return pointer==other.pointer; }
bool operator!=(const MyConstIterator& other) const { return pointer!= other.pointer; }
int const& operator*() const
{
if(pointer==-1)
return nullvalue;
else
return data[pointer];
}
private:
value_type* data = nullptr;
int length;
int pointer = 0;
value_type nullvalue = 0;
};
public:
typedef MyIterator iterator_type;
typedef MyConstIterator const_iterator_type;
MyClass()
{
data= new int[length];
for(int i=0; i<length;i++)
data[i]=i+1;
}
~MyClass() {
delete[] data;
}
iterator_type begin() { return MyIterator(data,length); }
iterator_type end() { return MyIterator(data,length,true); }
const_iterator_type begin() const { return MyConstIterator(data,length); }
const_iterator_type end() const { return MyConstIterator(data,length,true); }
};
namespace boost {
template<> struct range_mutable_iterator<MyClass> {
typedef MyClass::MyIterator type;
};
template<> struct range_const_iterator<MyClass> {
typedef MyClass::MyConstIterator type;
};
}
#include <iostream>
int main()
{
MyClass c;
BOOST_FOREACH(int i, c) {
std::cout << i << "\n";
}
}
¹ (unless you store the iterators somewhere else, but that would be a huge anti-pattern for many many reasons)
After doing some experimentation with move semantics with an array type I created, I am wondering why Microsoft's C++ compiler calls the move constructor when returning from a method by value whilst the Clang compiler elides the copy all together?
Is this correct or incorrect behaviour from Clang? or correct behaviour from Microsoft?
#include <algorithm>
#include <iostream>
template<typename T>
class Array {
public:
template<typename E>
class ArrayIterator {
public:
ArrayIterator(Array<E>& elements, int index) : position_(index), elements_(elements) {
}
T& operator * () {
return elements_[position_];
}
ArrayIterator& operator++ () {
position_++;
return *this;
}
ArrayIterator operator++ (int) {
return ArrayIterator(elements_, ++position_);
}
bool operator != (ArrayIterator const & other) {
return position_ != other.position_;
}
private:
int position_;
Array<E>& elements_;
};
typedef ArrayIterator<T> iterator;
Array();
explicit Array(int size);
~Array();
Array(const Array& other);
Array(Array&& other);
Array<T>& operator = (Array other);
T& operator[](int index);
int size() const;
iterator begin();
iterator end();
private:
void internal_swap(Array& other);
T *elements_;
int length_;
};
template<typename T>
Array<T>::Array() {
length_ = 0;
elements_ = 0;
}
template<typename T>
Array<T>::Array(int size) {
elements_ = new T[size];
length_ = size;
}
template<typename T>
Array<T>::~Array() {
delete[] elements_;
std::cout << "Destroy...." << std::endl;
}
template<typename T>
Array<T>::Array(const Array<T>& other) {
std::cout << "copy ctor" << std::endl;
length_ = other.size();
T *elements = new T[size()];
std::copy(other.elements_, other.elements_ + other.size(), elements);
elements_ = elements;
}
template<typename T>
Array<T>::Array(Array<T>&& other) {
std::cout << "move ctor" << std::endl;
length_ = other.size();
T* oelements = other.elements_;
other.elements_ = 0;
this->elements_ = oelements;
}
template<typename T>
Array<T>& Array<T>::operator = (Array other) {
internal_swap(other);
return *this;
}
template<typename T>
T& Array<T>::operator[](int index) {
return elements_[index];
}
template<typename T>
int Array<T>::size() const {
return length_;
}
template<typename T>
typename Array<T>::iterator Array<T>::begin() {
return iterator(*this, 0);
}
template<typename T>
typename Array<T>::iterator Array<T>::end() {
return iterator(*this, size());
};
template<typename T>
void Array<T>::internal_swap(Array& other){
T* oelements = other.elements_;
other.elements_ = this->elements_;
this->elements_ = oelements;
}
Array<int> get_values(int x);
int main(int argc, const char *argv[]) {
Array<int> a = get_values(2);
for (Array<int>::iterator i = a.begin(); i != a.end(); ++i) {
std::cout << *i << std::endl;
}
return 0;
}
Array<int> get_values(int x) {
Array<int> a(10);
if(x == 1) return a;
for (int i = 0; i <= 9; i++) {
a[i] = 1 + i;
}
return a;
}
Copy elision is one of those rare optimizations where the standard allows different observable behavior (it doesn't fall under the as-if rule), yet isn't undefined behavior.
Whether any copy or move constructor is called or elided in this context is unspecified, and different compilers can behave differently and both be correct.