own iterator class not working with std::binary_search - c++

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>.

Related

"lvalue required as increment operand" about C++ & stl vector's begin()

I'm trying to implement a vector(just like the one in STL).
Here is part of my code
template <class T, class alloc>
class vector
{
public:
typedef T value_type;
typedef const value_type * const_iterator;
typedef value_type * iterator;
iterator start;
iterator finish;
iterator end_of_storage;
iterator begin() {return start;}
iterator end() {return finish;}
...
}
And when I'm trying compile codes below
vector<char> characters(2, 2);
cout << *++(characters.begin()) << endl;
Here comes the error
error: lvalue required as increment operand
I'm not sure what happened, I think characters.begin() should be a lvalue. I've looked at the one in SGI STL and I couldn't find anything.
Many thanks for any advice.
This issue arises because you are using raw pointers as iterators. When you return a raw pointer (or other primitive type, such as int) from function it is not a modifiable value. However it can be easily fixed by using class iterator:
template<typename T>
struct base_iterator
{
T * m_p_value;
base_iterator &
operator ++(void)
{
++m_p_value;
return *this;
}
T &
operator *(void)
{
return *m_p_value;
}
};
// inside of vector
typedef base_iterator< value_type > iterator;
online compiler

checked_array_iterator<T> in C++11

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.

How to write a C++11 template that can take a const iterator

In responding to this question on CodeReview, I was thinking about how one might write a template function to indicate const-ness of a contained object.
To be specific, consider this templated function
#include <iostream>
#include <numeric>
#include <vector>
template <class It>
typename std::iterator_traits<It>::value_type average(It begin, It end) {
typedef typename std::iterator_traits<It>::value_type real;
real sum = real();
unsigned count = 0;
for ( ; begin != end; ++begin, ++count)
sum += *begin;
return sum/count;
}
int main()
{
std::vector<double> v(1000);
std::iota(v.begin(), v.end(), 42);
double avg = average(v.cbegin(), v.cend());
std::cout << "avg = " << avg << '\n';
}
It takes an iterator and calculates an average based on the contained numbers, but it is guaranteed not to modify the vector through the passed iterators. How does one convey this to a user of the template?
Note that declaring it like this:
template <class It>
typename std::iterator_traits<It>::value_type average(const It begin,
const It end)
doesn't work because it's not the iterator, but the thing the iterator points to, that's const. Do I have to wait for concepts to be standardized?
Note that I don't want to require const iterators, but instead to indicate that they may be safely used here. That is, rather than restricting the caller, I want to convey a promise that my code is making: "I will not modify your underlying data."
template <class ConstIt>
It's that simple. There's nothing to be enforced on the caller side here, as a non-const iterator is also usable for const access, so it's just API documentation, and that's what your choice of parameter identifier is - API documentation.
That does lead on to the question of enforcement on the callee/function side - so it can't be pretending it will only use the iterator for const access then modify elements anyway. Should you care about that, you could accept the parameter using some identifier making it clear it wasn't meant to be used everywhere throughout the function, then create a const_iterator version with a more convenient identifier. That could be tricky as in general you don't know if the iterator type is a member of a container, let alone what that container type is and whether it has a const_iterator too, so some manner of Concepts would indeed be ideal - fingers crossed for C++14. Meanwhile:
have your caller tell you the container type,
write your own traits, OR
write a simple wrapper class that holds an iterator and ensures only const access to the referenced data escapes the interface
This last wrapper approach is illustrated below (not all of the iterator API is implemented so flesh out as needed):
template <typename Iterator>
class const_iterator
{
public:
typedef Iterator iterator_type;
typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
// note: trying to add const to ...:reference or ..:pointer doesn't work,
// as it's like saying T* const rather than T const* aka const T*.
typedef const typename std::iterator_traits<Iterator>::value_type& reference;
typedef const typename std::iterator_traits<Iterator>::value_type* pointer;
const_iterator(const Iterator& i) : i_(i) { }
reference operator*() const { return *i_; }
pointer operator->() const { return i_; }
bool operator==(const const_iterator& rhs) const { return i_ == rhs.i_; }
bool operator!=(const const_iterator& rhs) const { return i_ != rhs.i_; }
const_iterator& operator++() { ++i_; return *this; }
const_iterator operator++(int) const { Iterator i = i_; ++i_; return i; }
private:
Iterator i_;
};
Sample usage:
template <typename Const_Iterator>
void f(const Const_Iterator& b__, const Const_Iterator& e__)
{
const_iterator<Const_Iterator> b{b__}, e{e__}; // make a really-const iterator
// *b = 2; // if uncommented, compile-time error....
for ( ; b != e; ++b)
std::cout << *b << '\n';
}
See it running at ideone.com here.
You may add some traits to see if the iterator is a const_iterator:
template <typename IT>
using is_const_iterator =
std::is_const<typename std::remove_reference<typename std::iterator_traits<IT>::reference>::type>;
And then use something like:
template <typename IT>
typename
std::enable_if<is_const_iterator<IT>::value,
typename std::iterator_traits<It>::value_type
>::type average(It begin, It end);
But this will avoid the use of iterator which are convertible to const_iterator.
So it will be better to restrict iterator when const is forbidden (as in std::sort)
It takes an iterator and calculates an average based on the contained numbers, but it is guaranteed not to modify the vector through the passed iterators. How does one convey this to a user of the template?
You could use SFINAE to disable the template when non-const iterators are passed, but that would be an unnecessary limitation.
Another way is to accept ranges instead of iterators. This way you could write:
template <class Range>
typename Range::value_type average(Range const& range);
The user can pass a container or iterator range in there.
You could try always dereferencing the iterator through some function deref().
template <typename It>
typename ::std::remove_reference<typename ::std::iterator_traits<It>::reference>::type const&
deref(It it)
{
return *it;
}
Which would guarantee the underlying value will not be modified.

C++11 cast const iterator pointing to container of shared_ptr objects

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.

How to avoid code duplication implementing const and non-const iterators?

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.