C++11 provides std::array<T> to wrap C arrays, but only where you know the array size at compile time. What is the best way of handling arrays whose size is only known at runtime?
Background
I'm porting some code from MSVC to GCC. MSVC provides the stdext::checked_array_iterator<T> template to provide some protection for lines of code such as this:
std::copy(v.begin(), v.end(), stdext::checked_array_iterator<int*>(arr, numVals));
So far I can think of two options: abandoning the safety check or writing my own implementation. On that note, I'd be grateful for any constructive comments on this implementation:
namespace stdext {
template<typename T>
struct checked_array_iterator
{
private:
T _val;
size_t _len;
public:
typedef typename std::remove_pointer<T>::type value_type;
checked_array_iterator(T val, size_t len) : _val(val), _len(len) {}
checked_array_iterator<T> operator++(int)
{
if(_len == 0)
throw std::range_error("Array iterator overrun");
checked_array_iterator<T> retval = *this;
_val++;
_len--;
return retval;
}
checked_array_iterator<T> & operator++()
{
if(_len == 0)
throw std::range_error("Array iterator overrun");
_val++;
_len--;
return *this;
}
value_type & operator*()
{
return *_val;
}
bool operator==(checked_array_iterator<T>& other) { return other._val == _val; }
bool operator!=(checked_array_iterator<T>& other) { return !(other == *this); }
T operator->() { return _val; }
};
}
namespace std
{
template <typename T>
struct iterator_traits<stdext::checked_array_iterator<T>>
{
typedef std::ptrdiff_t difference_type;
typedef typename std::remove_pointer<T>::type value_type;
typedef T pointer;
typedef value_type& reference;
typedef std::input_iterator_tag iterator_category;
};
}
Would this be so bad?
if (v.size() > numVals)
throw std::runtime_error("oops");
std::copy(v.begin(), v.end(), arr);
It's more efficient too, because it checks just once that the size is OK, rather than once per element.
In some cases, you could use Eigen Map (or write a similar implementation). It is essentially a vector which does not manage its own storage but gets pointer and size in its constructor. In that regard, it is quite similar to the level of safety provided by stdext::checked_array_iterator<T>. On the other hand, it is not meant to be an iterator, it is meant to be a matrix (or vector as a special case) class. Ane Eigen is both free and multi-platform.
Related
The title of this question used to be: Are there practical advantages to creating an iterator class compared to returning raw pointers from begin and end functions?
Recently I have been working on a code base which uses MFC and objects such as CArray<T, U>.
Some parts of new code which has been written make use of the STL and <algorithm> library.
For example
CArray<int int> carray;
carray // do stuff
std::vector<int> stlvector(begin(carray), end(carray));
stlvector.dostuff() // do stuff
I recently asked a question about creating iterators for a class such as CArray, which I do not have access to.
I now have some further questions about this. The first question can be found here. Here is my second question:
Should the begin and end functions return raw pointers or iterators?
In the linked question above, an example was provided as an answer which returns raw pointers. This answer was very similar to the implementation I used.
template<typename T, typename U>
auto begin(const CArray<T, U> &array>)
{
return &array[0];
}
template<typename T, typename U>
auto end(const CArray<T, U> &array>)
{
return (&array[array.GetCount() - 1]) + 1;
}
These functions return raw pointers. However I attempted to implement an iterator solution. So far I have not been successful.
The main reference which I used during my research can be found here:
https://internalpointers.com/post/writing-custom-iterators-modern-cpp
First attempt
This is the first attempt that I made in finding a solution.
You can play with this code here.
#include <iostream>
#include <iterator>
#include <algorithm>
template <typename U>
class CArrayForwardIt
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = U;
using pointer = U*;
using reference = U&;
public:
CArrayForwardIt(pointer ptr)
: m_ptr(ptr)
{
}
// = default?
//CArrayForwardIt(CArrayForwardIt<U> other)
// : m_ptr(ptr)
// {
// }
reference operator*() const
{
return *m_ptr;
}
// what does this do, don't understand why operator-> is needed
// or why it returns a U* type
pointer operator->()
{
return m_ptr;
}
CArrayForwardIt& operator++()
{
++ m_ptr;
return *this;
}
CArrayForwardIt operator++(int)
{
CArrayForwardIt tmp(*this);
++ (*this);
return tmp;
}
friend bool operator==(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return lhs.m_ptr == rhs.m_ptr;
}
friend bool operator!=(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return !(lhs == rhs);
}
private:
pointer m_ptr;
};
template<typename T, typename U>
auto begin(const CArray<T, U> &array)
{
return CArrayForwardIt<U>(&array[0]);
}
template<typename T, typename U>
auto end(const CArray<T, U> &array)
{
return CArrayForwardIt<U>((&array[array.GetCount() - 1]) + 1);
}
int main()
{
CArray<int, int> c;
// do something to c
std::vector<int> v(begin(c), end(c));
return 0;
}
This is what happens when I try to compile this (with Visual Studio 2019 Pro).
no instance of constructor "std::vector<_Ty, _Alloc>::vector [with _Ty=int, _Alloc=std::allocator<int>]" matches argument list
'<function-style-cast>': cannot convert from 'contt TYPE*' to 'std::CArrayForwardIt<U>'
'std::vector<int, std::allocator<int>>::vector(std::vector<int, std::allocator<int>> &&, const _Alloc &) noexcept(<expr>)': cannot convert from argument 1 from 'void' to 'const unsigned int'
Being more familiar with gcc, I have little knowledge of how to understand this.
Second attempt
I made another two further attempts at this but they were quite similar.
One was to change my class CArrayForwardIt to inherit from iterator<std::forward_iterator_tag, std::ptrdiff_t, U, U*, U&>, and to remove the using... lines at the top of the class. This didn't seem to get me any closer to a solution.
In addition, I looked at the constructor definition for std::vector. See here.
I may be misunderstanding here, but it looks like std::vector requires a InputIt type argument.
Therefore I tried to change my class to be something like this:
#include <iostream>
#include <iterator>
#include <algorithm>
template <typename U>
class forward_iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = U;
using pointer = U*;
using reference = U&;
public:
forward_iterator(pointer ptr)
: m_ptr(ptr)
{
}
// = default?
//forward_iterator(forward_iterator<U> other)
// : m_ptr(ptr)
// {
// }
reference operator*() const
{
return *m_ptr;
}
// what does this do, don't understand why operator-> is needed
// or why it returns a U* type
pointer operator->()
{
return m_ptr;
}
forward_iterator& operator++()
{
++ m_ptr;
return *this;
}
forward_iterator operator++(int)
{
forward_iterator tmp(*this);
++ (*this);
return tmp;
}
friend bool operator==(const forward_iterator& lhs, const forward_iterator& rhs)
{
return lhs.m_ptr == rhs.m_ptr;
}
friend bool operator!=(const forward_iterator& lhs, const forward_iterator& rhs)
{
return !(lhs == rhs);
}
private:
pointer m_ptr;
};
template<typename T, typename U>
auto begin(const CArray<T, U> &array)
{
return forward_iterator<U>(&array[0]);
}
template<typename T, typename U>
auto end(const CArray<T, U> &array)
{
return forward_iterator<U>((&array[array.GetCount() - 1]) + 1);
}
int main()
{
CArray<int, int> c;
// do something to c
std::vector<int> v(begin(c), end(c));
return 0;
}
This, perhaps unsurprisingly, did not compile either. At this point I became confused. std::vector appears to demand an InputIt type, which forward_iterator should work for, but it doesn't seem to make sense to redefine what forward_iterator is, even if I write this class outside of namespace std.
Question
I am fairly sure there should be a way to write an iterator class for the MFC CArray, which can be returned by begin and end functions. However, I am confused as to how to do this.
Further to the question of writing a working solution, I am beginning to wonder if there are any practical advantages to doing this? Does what I am trying to do even make sense? The raw pointer solution clearly works, so are there any advantages of investing the effort to write an iterator based solution? Can iterator solutions provide more sophisticated bounds checking, for example?
Since I managed to get this working I wanted to post a solution, hopefully I don't make too many errors transcribing it.
One thing that was not shown in the above code snippet is the fact that all these class and function definitions existed inside of namespace std. I posted another question about this earlier, and was informed that these things should not be inside namespace std. Correcting this seems to have resolved some problems and made the solution a step closer.
You can find that question here.
This is what I have so far: This is how to write an iterator for an external class which the programmer does not have access to. It also works for your own custom types or containers which you do have access to.
// How to write an STL iterator in C++
// this example is specific to the MFC CArray<T, U> type, but
// it can be modified to work for any type, note that the
// templates will need to be changed for other containers
#include <iterator>
#include <mfc stuff...>
template<typename T, typename U>
class CArrayForwardIt : public std::iterator<std::forward_iterator_tag, std::ptrdiff_t, U, U*, U&>
{
// the names used in this class are described in this list
// using iterator_category = std::forward_iterator_tag;
// using difference_type = std::ptrdiff_t;
// using value_type = U;
// using pointer = U*;
// using reference = U&;
public:
CArrayForwardIt(CArray<T, U> &array_ref, const std::size_t index)
: m_array_ref(array_ref)
, m_index(index)
{
}
// the only way I could get this to work was to make the return type
// an explicit U&, I don't know why this is required, as using
// reference operator*() const did not seem to work
U& operator*() const
{
if(m_index < m_array_ref.GetCount())
{
return m_array_ref[m_index];
}
else
{
throw std::out_of_range("Out of range Exception!");
}
}
CArrayForwardIt& operator++()
{
++ m_index;
return *this;
}
CArrayForwardIt operator++(int)
{
CForwardArrayIt tmp(*this);
++(*this);
}
friend bool operator==(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
if(&(lhs.m_array_ref) == &(rhs.m_array_ref))
{
return lhs.m_index == rhs.m_index;
}
return false;
}
friend bool operator!=(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return !(lhs == rhs);
}
private:
std::size_t m_index;
CArray<T, U> &m_array_ref;
};
template<typename T, typename U>
auto begin(CArray<T, U> &array)
{
return CArrayForwardIt<T, U>(array, 0);
}
template<typename T, typename U>
auto end(CArray<T, U> &array)
{
return CArrayForwardIt<T, U>(array, array.GetCount());
}
int main()
{
CArray<int, int> array;
// do stuff to array
// construct vector from elements of array in one line
std::vector<int> vector(begin(array), end(array));
// also works with other STL algorithms
}
Note my comment about the U& operator* which produced some compiler error when written as reference operator* which might be a Visual Studio compiler bug. I'm not sure about this.
I would suggest that although this method is more difficult to implement (but not much when you know how to do it) it has the advantage of not using raw pointers which means that the iterator functions can provide proper exception throwing statements when illegal operations are attempted. For example, incrementing the iterator when it is already at the end.
Useful references:
https://lorenzotoso.wordpress.com/2016/01/13/defining-a-custom-iterator-in-c/
https://internalpointers.com/post/writing-custom-iterators-modern-cpp
For completeness, here is the simpler solution using raw pointers.
template<typename T, typename U>
auto begin(CArray<T, U> &array)
{
return &(array[0]);
}
template<typename T, typename U>
auto end(CArray<T, U> &array)
{
// get address of last element then increment
// pointer by 1 such that it points to a memory
// address beyond the last element. only works for
// storage containers where higher index elements
// are guaranteed to be at higher value memory
// addresses
if(array.GetCount() > 0)
{
return &(array[array.GetCount() - 1]) + 1;
}
else
{
return &(array[0]) + 1;
}
}
You can use these in the same way as demonstrated in the other answer, however there is also a way to use STL vector without the begin and end functions:
CArray<int, int> array; // ... do something to array
std::vector<int> vec(&array[0], &(array[array.GetCount() - 1]) + 1);
// note only works if elements guaranteed to be in continuous
// packed memory locations
but it also works with begin and end which is nicer
std::vector<int> vec(begin(array), end(array));
I have an STL container whose element type is const std::shared_ptr<MyClass>.
I want to supply two iterator types to the user:
MyContainer::iterator
typedefed as std::vector<const std::shared_ptr<MyClass>>::iterator
(which should be the same type as std::vector<const std::shared_ptr<const MyClass>>::const_iterator
MyContainer::const_iterator
typedefed as std::vector<const std::shared_ptr<const MyClass>>::iterator
(which should be the same type as std::vector<const std::shared_ptr<const MyClass>>::const_iterator
In other words, I want the "const" to refer to the MyClass constness, not shared_ptr constness. The solution I found for getting the second iterator type is getting the first one, which is easy (e.g. using vector::begin), and then converting it to the second type using static_cast (fixme: no need to use const_cast because I'm adding constness, not removing it).
Would that be the common good-design way to achieve that, or there's a better/more common way?
typedefed as std::vector<const std::shared_ptr<MyClass>>::iterator (which should be the same type as std::vector<std::shared_ptr<const MyClass>>::const_iterator
But it probably isn't the same type. Iterators are not just pointers. If the iterator and const_iterator types are defined inside vector then they are completely unrelated types:
template<typename T>
class vector
{
class iterator;
class const_iterator;
// ...
vector<const int> is a different type to vector<int> and so their nested types are also different. As far as the compiler is concerned they are completely unrelated types, i.e. you cannot just move const around to any point in this type and get compatible types:
vector<const shared_ptr<const T>>::iterator
You cannot use const_cast to convert between unrelated types. You can use static_cast to convert a vector<T>::iterator to a vector<T>::const_iterator but it's not really a cast, you're constructing the latter from the former, which is allowed because that conversion is required by the standard.
You can convert a shared_ptr<const T> to a shared_ptr<T> with const_pointer_cast<T> but again only because it's defined to work by the standard, not because the types are inherently compatible and not because it "just works" like plain ol' pointers.
Since vector's iterators don't provide the deep-constness you want, you'll need to write your own, but it's not hard:
class MyClass { };
class MyContainer
{
typedef std::vector<std::shared_ptr<MyClass>> container_type;
container_type m_cont;
public:
typedef container_type::iterator iterator;
class const_iterator
{
typedef container_type::const_iterator internal_iterator;
typedef std::iterator_traits<internal_iterator> internal_traits;
const_iterator(internal_iterator i) : m_internal(i) { }
friend class MyContainer;
public:
const_iterator() { }
const_iterator(iterator i) : m_internal(i) { }
typedef std::shared_ptr<const MyClass> value_type;
typedef const value_type& reference;
typedef const value_type* pointer;
typedef internal_traits::difference_type difference_type;
typedef internal_traits::iterator_category iterator_category;
const_iterator& operator++() { ++m_internal; return *this; }
const_iterator operator++(int) { const_iterator tmp = *this; ++m_internal; return tmp; }
reference operator*() const { m_value = *m_internal; return m_value; }
pointer operator->() const { m_value = *m_internal; return &m_value; }
// ...
private:
internal_iterator m_internal;
mutable value_type m_value;
};
iterator begin() { return m_cont.begin(); }
const_iterator begin() const { return const_iterator(m_cont.begin()); }
// ...
};
That iterator type is mising a few things (operator--, operator+) but they're easy to add, following the same ideas as already shown.
The key point to notice is that in order for const_iterator::operator* to return a reference, there needs to be a shared_ptr<const MyClass> object stored as a member of the iterator. That member acts as a "cache" for the shared_ptr<const MyClass> value, because the underlying container's real elements are a different type, shared_ptr<MyClass>, so you need somewhere to cache the converted value so a reference to it can be returned. N.B. Doing this slightly breaks the iterator requirements, because the following doesn't work as expected:
MyContainer::const_iterator ci = c.begin();
const shared_ptr<const MyClass>& ref = *ci;
const MyClass* ptr = ref.get();
++ci;
(void) *ci;
assert( ptr == ref.get() ); // FAIL!
The reason the assertion fails is that *ci doesn't return a reference to an underlying element of the container, but to a member of the iterator, which gets modified by the following increment and dereference. If this behaviour isn't acceptable you'll need to return a proxy from your iterator instead of caching a value. Or return a shared_ptr<const MyClass> when the const_iterator is dereferenced. (The difficulties of getting this 100% right is one of the reasons STL containers don't try to model deep constness!)
A lot of the effort of defining your own iterator types is done for you by the boost::iterator_adaptor utility, so the example above is only really useful for exposition. With that adaptor you'd only need to do this to get your own custom iterator types with the desired behaviour:
struct iterator
: boost::iterator_adaptor<iterator, container_type::iterator>
{
iterator() { }
iterator(container_type::iterator i) : iterator_adaptor(i) { }
};
struct const_iterator
: boost::iterator_adaptor<const_iterator, container_type::const_iterator, std::shared_ptr<const MyClass>, boost::use_default, std::shared_ptr<const MyClass>>
{
const_iterator() { }
const_iterator(iterator i) : iterator_adaptor(i.base()) { }
const_iterator(container_type::const_iterator i) : iterator_adaptor(i) { }
};
boost::iterator_adaptor makes it pretty easy to define your own iterator types based on another iterator type. So you can set it up so that *iter is a const shared_ptr<MyClass>& or const shared_ptr<const MyClass>& as desired.
Though in the const_iterator case, dereferencing can't return a const shared_ptr<const MyClass>& if what you actually have is shared_ptr<MyClass>. So we'll define const_iterator::reference as just shared_ptr<const MyClass> and return by value.
#include <boost/iterator/iterator_adaptor.hpp>
class MyContainer {
public:
class iterator;
class const_iterator;
class iterator :
public boost::iterator_adaptor<
iterator, // This class, for CRTP
std::vector<const std::shared_ptr<MyClass>>::const_iterator,
// Base type
const std::shared_ptr<MyClass> > // value_type
{
public:
iterator() {}
iterator(const iterator&) = default;
private:
friend class MyContainer; // allow private constructor
friend class boost::iterator_core_access; // allow dereference()
explicit iterator(base_type iter) : iterator_adaptor(iter) {}
const std::shared_ptr<MyClass>& dereference() const
{ return *base_reference(); }
};
class const_iterator :
public boost::iterator_adaptor<
const_iterator, // This class, for CRTP
std::vector<const std::shared_ptr<MyClass>>::const_iterator,
// Base type
const std::shared_ptr<const MyClass>, // value_type
boost::use_default, // difference_type
std::shared_ptr<const MyClass> > // reference_type
{
public:
const_iterator();
const_iterator(const const_iterator&) = default;
// Implicit conversion from iterator to const_iterator:
const_iterator(const iterator& iter) : iterator_adaptor(iter.base()) {}
private:
friend class MyContainer; // allow private constructor
friend class boost::iterator_core_access; // allow dereference()
explicit const_iterator(base_type iter) : iterator_adaptor(iter) {}
std::shared_ptr<const MyClass> dereference() const
{ return *base_reference(); }
};
iterator begin() { return iterator(mVec.begin()); }
iterator end() { return iterator(mVec.end()); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
const_iterator cbegin() const { return const_iterator(mVec.begin()); }
const_iterator cend() const { return const_iterator(mVec.end()); }
private:
std::vector<const std::shared_ptr<MyClass>> mVec;
};
shared_ptr and other standard smart pointers are not designed with deep-constness in mind. They are trying to be as close to raw pointer usage as possible, and raw pointer's const-ness does not affect the pointee's const-ness.
Andrei Alexandrescu's Loki::SmartPtr (described in his Modern C++ Design) implements reference counting and deep const-ness as policies, which would give you the effect you're looking for. If you don't mind switching to a non-standard smart pointer in order to get non-standard behavior, that may be one way to go.
I found a lot of answers here to the topic, but I couldn't get my code to run.
EDIT:
The posted example now works after introducing the missing things. In hope some one can use the example as basis for own experiments. I also introduced the missing things to use this example as random access iterator. It works much more efficient with binary_search algos.
If I have to write my own iterator I struggle with value_type and other "specials" to operate with .
I read a lot of articles here how to NOT write iterators but could not get any working example. Especially I read that I should not derive from iterator. So I will ask stupid again:
How can I define the value_type of my iterator. It did not work with in class definition and also did not work with defining manually over type_traits struct. No idea how to continue...
#include <iostream>
#include <algorithm>
#include <type_traits>
#include <iterator>
using namespace std;
int data[]= { 1,4,7,9,11,20,28 }; //Sorted data
template < typename T >
class MyIter
{
int offset;
T* base;
public:
typedef int value_type;
//add the following lines after reading the answers -> it works!
typedef std::ptrdiff_t difference_type;
typedef T * pointer;
typedef T & reference;
typedef std::forward_iterator_tag iterator_category;
// if you want to use as random access iterator:
// typedef std::random_access_iterator_tag iterator_category;
public:
MyIter( T* _base, int _offset) : base(_base), offset(_offset) {}
MyIter() {}
bool operator !=( const MyIter& rhs)
{
T* tmp1= base+offset;
T* tmp2= rhs.base + rhs.offset;
return tmp1 != tmp2;
}
MyIter operator++(int)
{
MyIter tmp(*this);
offset++;
return tmp;
}
T operator*()
{
return *(base+offset);
}
// Addition: if used as random access iterator you must provide:
int operator-(const MyIter& rhs)
{
return offset-rhs.offset;
}
MyIter operator+=(int off)
{
offset+=off;
return *this;
}
};
typedef MyIter<int> iterType ;
int main()
{
cout << "ok" << endl;
pair<iterType, iterType> bounds;
MyIter<int> start( data,0);
MyIter<int> ende ( data,7);
bounds = equal_range( start, ende, 28 );
for ( iterType it= bounds.first; it!=bounds.second; it++)
{
cout << "Found " << *it << endl;
}
return 0;
}
You are missing a few definitions in addition to value_type that the standard library requires from iterators:
typedef std::ptrdiff_t difference_type;
typedef T * pointer;
typedef T & reference;
typedef std::forward_iterator_tag iterator_category;
Alternatively, inheriting from std::iterator<std::forward_iterator_tag, T> will give you these. I don't know where you read that you shouldn't do that, but that's exactly what std::iterator is for.
Additionally, you're missing a pre-increment operator:
MyIter operator++()
{
++offset;
return *this;
}
and also -> and == operators. Also, the dereference operator should probably return a reference to allow *it = 42, with a const overload returning a value or const reference.
std::binary_search requires a random access iterator. Which
means that the iterator must support addition and subtraction
(+, +=, - and -=), with exactly the same semantics as
a pointer. And any number of algorithms will also expect
a number of typedef in the iterator: deriving from
std::iterator is the easiest way to get them. (Technically,
what the standard requires is that std::iterator_traits yield
the correct values, so you could explicitly instantiate it for
your iterator. But its default implementation picks up typedef
in the iterator class.)
EDIT:
Rereading your post: you definitly should publically derive from std::iterator. Whoever says otherwise is wrong.
You are missing several essential type members and the pre-increment operator (as well as a bunch of other dereference and comparison operators as pointed out by #MikeSeymour). Add thise to your class definition (note that I rewrote the post-increment operators in terms of the currently missing pre-increment operator) to get your binary_search going
typedef std::forward_iterator_tag iterator_category;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
MyIter& operator++()
{
++offset;
return *this;
}
MyIter operator++(int)
{
MyIter tmp(*this);
++*this; // NOTE this will call the other operator++
return tmp;
}
Output on LiveWorkSpace As pointed out by #JamesKanze, these missing typedefs are provided by inheriting from std::iterator<std::forward_iterator_tag, T>.
Hi can anyone tell me why VS2010 gives me an error with this code, I can't see what's the problem with it?
Error code : error C2679: binary '=' : no operator found which takes a right-hand operand of type 'std::vector<_Ty>' (or there is no acceptable conversion)
// Elements container
typedef std::vector<CFVFElementPtr> ElementArray;
typedef std::map<CVertexSemantic::Type, ElementArray> ElementsMap;
// Create an empty array of elements
ElementsMap::value_type::second_type allElements;
// Concatinate each std::vector found within the map
std::transform(m_elementsMap.begin(), m_elementsMap.end(),
std::insert_iterator<ElementArray>(allElements, allElements.end()),
select2nd<ElementsMap::value_type>() );
All I am trying to do is this
for (auto i = m_elementsMap.begin(); i != m_elementsMap.end(); ++i)
{
const ElementArray& elements = (*i).second;
allElements.insert(allElements.end(), elements.begin(), elements.end());
}
As a response to Pablo, I tried creating a custom iterator that accepts an array of ElementArray, but I'm now getting a bucket load of errors.
template < class Container >
class container_insert_interator
{
public:
typedef container_insert_interator<Container> this_type;
typedef Container container_type;
typedef typename Container::const_reference const_reference;
typedef typename Container::value_type valty;
explicit container_insert_interator (Container& cont, typename Container::iterator iter)
: container(&cont), iter(iter)
{ }
this_type& operator = (typename const_reference value)
{
iter = container->insert( iter, std::begin(value), std::end(value) );
++iter;
return *this;
}
this_type& operator* ()
{
return *this;
}
this_type& operator++ ()
{
return *this;
}
this_type operator++ (int)
{
return *this;
}
protected:
Container* container; // pointer to container
typename Container::iterator iter ; // iterator into container
};
The problem is ElementsMap::value_type::second_type is ElementArray. That is, you're trying to insert instances of ElementArray into an ElementArray, which really holds instances of CFVFElementPtr.
Update: Changing your declaration of operator= from
this_type& operator = (typename const_reference value)
to
template<typename OtherContainer>
this_type& operator = ( OtherContainer const& value)
almost works. Except that neither VS 2010 or GCC 4.6.1 provide the vector::insert overload that returns an iterator. You'll probably need a newer compiler if you want your ranged insert to return an iterator to replace iter.
Changing the implementation to always insert at the end, namely
container->insert( container->end(), std::begin(value), std::end(value) );
in operator= compiles fine with GCC 4.6.1 (and, of course, you can remove all references to iter in your iterator class).
You might consider using a functor that can append elements to your target array along with std::for_each to walk the map:
struct ElementArray_appender
{
ElementArray_appender( ElementArray& dest_) : dest(dest_) {
};
void operator()( ElementsMap::value_type const& map_item) {
ElementArray const& vec( map_item.second);
dest.insert( dest.end(), vec.begin(), vec.end());
};
private:
ElementArray& dest;
};
// in whatever function:
std::for_each( m_elementsMap.begin(), m_elementsMap.end(), ElementArray_appender(allElements));
I found the answer, the poblem was with my custom iterator. The correct one that works is
template < class Container >
class container_back_insert_interator
{
public:
typedef container_back_insert_interator<Container> this_type;
typedef Container container_type;
typedef typename container_type::const_reference const_reference;
typedef typename container_type::value_type valty;
explicit container_back_insert_interator (container_type& cont)
: container(&cont)
{ }
this_type& operator = (const_reference value)
{
container->insert( container->end(), std::begin(value), std::end(value) );
return *this;
}
this_type& operator* ()
{
return *this;
}
this_type& operator++ ()
{
return *this;
}
this_type operator++ (int)
{
return *this;
}
protected:
container_type* container; // pointer to container
};
template < class Container >
inline container_back_insert_interator<Container> container_back_inserter(Container& cont)
{
return container_back_insert_interator<Container>(cont);
}
However, I must warn that if you do use this in Visual Studio 2010 you will have to implement an SGI form of std::transform. The version that ships with VS2010 for some reason throws numerous errors, all of which lie within the <xutility> header.
This std::transform works just fine.
I'm implementing a custom container with an STL-like interface. I have to provide a regular iterator and a const iterator. Most of the code for the two versions of the iterators is identical . How can I avoid this duplication?
For example, my container class is Foo, and I'm implementating FooIterator and FooConstIterator. Both of the iterators have to provide methods like operator++() which are identical.
My question is similar to How do I remove code duplication between similar const and non-const member functions?, but the answer to that one is specific to const and non-const methods, especially accessors. I don't see how that might generalize to the iterator problem.
Should I have FooIterator derive from FooConstIterator and extend it with additional non-const methods? That either leads to virtual methods or method hiding, which seem inappropriate here.
Perhaps FooIterator should contain a FooConstIterator. Although that approach does reduce implementation duplication, it seems to re-introduce a lot of boilerplate method definitions.
Is there clever template technique for generating the two iterators from a single definition? Or perhaps there's a way to--shudder--use the preprocessor to stamp out these nearly identical classes.
I've tried looking at my local STL implementation to see how it handle this. There are so many helper classes that I'm having trouble grokking the design, but it looks like the functionality is simply duplicated.
In previous projects, my custom container was built on top of a standard STL container, so I didn't have to provide my own iterators. That's not an option in this case.
[The best answer was, unfortunately, deleted by a moderator because it was a link-only answer. I understand why link-only answers are discouraged; deleting it, however, has robbed future seekers of very useful information. The link has remained stable for more than seven years and continues to work at the time of this writing.]
I strongly recommend the original Dr. Dobb's Journal article by Matt Austern entitled "The Standard Librarian: Defining Iterators and Const Iterators", January 2001. Should that link go bad, now that Dr. Dobb's has ceased operating, it's also available here.
To prevent this replacement answer from being deleted, I will summarize the solution.
The idea is to implement the iterator once as a template that takes an extra template parameter, a boolean that says whether or not this is the const version. Anywhere in the implementation where the const and non-const versions differ, you use a template mechanism to select the correct code. Matt Austern's mechanism was called choose. It looked like this:
template <bool flag, class IsTrue, class IsFalse>
struct choose;
template <class IsTrue, class IsFalse>
struct choose<true, IsTrue, IsFalse> {
typedef IsTrue type;
};
template <class IsTrue, class IsFalse>
struct choose<false, IsTrue, IsFalse> {
typedef IsFalse type;
};
If you had separate implementations for const and non-const iterators, then the const implementation would include typedefs like this:
typedef const T &reference;
typedef const T *pointer;
and the non-const implementation would have:
typedef T &reference;
typedef T *pointer;
But with choose, you can have a single implementation that selects based on the extra template parameter:
typedef typename choose<is_const, const T &, T &>::type reference;
typedef typename choose<is_const, const T *, T *>::type pointer;
By using the typedefs for the underlying types, all the iterator methods can have an identical implementation. See Matt Austern's complete example.
Since C++11/14 you can avoid such little helpers an deduce the constness directly from a boolean template.
constness.h:
#ifndef ITERATOR_H
#define ITERATOR_H
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <iterator>
struct dummy_struct {
int hello = 1;
int world = 2;
dummy_struct() : hello{ 0 }, world{ 1 }{ }
};
template< class T >
class iterable {
public:
template< bool Const = false >
class my_iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
/* deduce const qualifier from bool Const parameter */
using reference = typename std::conditional_t< Const, T const &, T & >;
using pointer = typename std::conditional_t< Const, T const *, T * >;
protected:
pointer i;
public:
my_iterator( T* _i ) : i{ reinterpret_cast< pointer >( _i ) } { }
/* SFINAE enables the const dereference operator or the non
const variant
depending on bool Const parameter */
template< bool _Const = Const >
std::enable_if_t< _Const, reference >
operator*() const {
std::cout << "Const operator*: ";
return *i;
}
template< bool _Const = Const >
std::enable_if_t< !_Const, reference >
operator*() {
std::cout << "Non-Const operator*: ";
return *i;
}
my_iterator & operator++() {
++i;
return *this;
}
bool operator!=( my_iterator const & _other ) const {
return i != _other.i;
}
bool operator==( my_iterator const & _other ) const {
return !( *this != _other );
}
};
private:
T* __begin;
T* __end;
public:
explicit iterable( T* _begin, std::size_t _count ): __begin{ _begin }, __end{ _begin + _count } { std::cout << "End: " << __end << "\n"; }
auto begin() const { return my_iterator< false >{ __begin }; }
auto end() const { return my_iterator< false >{ __end }; }
auto cbegin() const { return my_iterator< true >{ __begin }; }
auto cend() const { return my_iterator< true >{ __end }; }
};
#endif
This can be used with something like that:
#include <iostream>
#include <array>
#include "constness.h"
int main() {
dummy_struct * data = new dummy_struct[ 5 ];
for( int i = 0; i < 5; ++i ) {
data[i].hello = i;
data[i].world = i+1;
}
iterable< dummy_struct > i( data, 5 );
using iter = typename iterable< dummy_struct >::my_iterator< false >;
using citer = typename iterable< dummy_struct >::my_iterator< true >;
for( iter it = i.begin(); it != i.end(); ++it ) {
std::cout << "Hello: " << (*it).hello << "\n"
<< "World: " << (*it).world << "\n";
}
for( citer it = i.cbegin(); it != i.cend(); ++it ) {
std::cout << "Hello: " << (*it).hello << "\n"
<< "World: " << (*it).world << "\n";
}
delete[] data;
}
STL uses inheritance
template<class _Myvec>
class _Vector_iterator
: public _Vector_const_iterator<_Myvec>
In addition to the suggestion that you might templatize the constness and non-constness, you could also reduce the amount of work by taking a look at Boost.Iterator tutorial - which also mentions the same solution.
You can use CRTP and a common base to "inject" methods (but you still have to duplicate ctors in current C++), or just use the preprocessor (no shuddering required; handles ctors easily):
struct Container {
#define G(This) \
This operator++(int) { This copy (*this); ++*this; return copy; }
// example of postfix++ delegating to ++prefix
struct iterator : std::iterator<...> {
iterator& operator++();
G(iterator)
};
struct const_iterator : std::iterator<...> {
const_iterator& operator++();
G(const_iterator)
};
#undef G
// G is "nicely" scoped and treated as an implementation detail
};
Use std::iterator, the typedefs it gives you, and any other typedefs you might provide to make the macro straight-forward.
Arthor O'Dwyer is answering this in detail in his blog post:
https://quuxplusone.github.io/blog/2018/12/01/const-iterator-antipatterns/
In essence,
template<bool IsConst>
class MyIterator {
int *d_;
public:
MyIterator(const MyIterator&) = default; // REDUNDANT BUT GOOD STYLE
template<bool IsConst_ = IsConst, class = std::enable_if_t<IsConst_>>
MyIterator(const MyIterator<false>& rhs) : d_(rhs.d_) {} // OK
};
using Iterator = MyIterator<false>;
using ConstIterator = MyIterator<true>;
};
Also, add static_assert(std::is_trivially_copy_constructible_v<ConstIterator>); to your code, to make sure your iterators stay trivially copy constructible:
Conclusion: If you are implementing your own container iterators — or any other pair of types with this “one-way implicit converting” behavior, such as the Networking TS’s const_buffers_type and mutable_buffers_type — then you should use one of the patterns above to implement converting constructors without accidentally disabling trivial copyability.