Note: I have written a "recipe" based on the lessons learnt from the exercise and the answers & comments on this page, see http://www.codeproject.com/Tips/1029941/Python-like-enumeration-in-Cplusplus.
I'm playing around with the extensions that C++11 brings to C++03. I want to be able to iterate over a container using the following code:
int main()
{
std::list<int> list = { 1, 2, 3, 4, 5, 6, 7 };
for (auto x : enumerated(list))
cout << x.first << " " << x.second << endl;
for (auto x : const_enumerated(list))
cout << x.first << " " << x.second << endl;
}
The first iteration has x modifiable, vs for the second, attempting to modify x would lead to a compile error. I have something that works for the non-const case:
template <typename Container>
class EnumerationAdaptor
{
public:
EnumerationAdaptor(Container& container) : container_(container) {}
EnumIter<typename Container::iterator> begin() const { return container_.begin(); }
EnumIter<typename Container::iterator> end() const { return container_.end(); }
private:
Container& container_;
};
template <typename Container>
EnumerationAdaptor<Container> enumerated(Container& container) { return container; }
template <typename Container>
EnumerationAdaptor<const Container> const_enumerated(const Container& container) { return container; }
The non-const case uses EnumIter<std::list<...>::iterator>, as desired, and I'm trying to make the const case use EnumIter<std::list<...>::const_iterator> as return type of begin() and end(). Seems like I need decltype:
template <typename Container>
class EnumerationAdaptor
{
public:
EnumerationAdaptor(Container& container) : container_(container) {}
EnumIter<decltype(Container().begin())> begin() const { return container_.begin(); }
EnumIter<decltype(Container().end())> end() const { return container_.end(); } // *** compile error (see below)
private:
Container& container_;
};
But I get a compilation error in Visual Studio 2015 Express:
Error C2440 'return': cannot convert from
'std::_List_const_iterator<std::_List_val<std::_List_simple_types<int>>>'
to
'EnumIter<std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>>'
[in c:\users\...\enumeratedcpp.cpp line 46, which is line marked ***]
which suggests that I'm doing something wrong with decltype, as the compiler is finding the non-const begin(). Is there a way to fix this?
EDIT: even with a simple EnumIter, problem is same:
template <typename Iter>
class EnumIter
{
public:
EnumIter(Iter begin) : iter_(begin) {}
EnumIter& operator++()
{
return *this;
}
bool operator!=(const EnumIter& rhs)
{
return iter_ != rhs.iter_; // or self.index_ != rhs.index_;
}
int operator*() const
{
return index_;
}
private:
Iter iter_;
int index_ = 0;
};
There is one issue with this expression:
decltype(Container().begin())
which is that Container() only works if Container happens to be default-constructible. That limits the usability of your class for no reason. (There is a lesser issue which is that this won't work for raw arrays, but that's another exercise).
Besides that, the code is perfectly valid for class types. From [expr.type.conv]:
The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object
type or the (possibly cv-qualified) void type, creates a prvalue of the specified type [...]
So if Container is const list<int>, then the type of that whole expression should be list<int>::const_iterator. If MSVC gives you something else, that's a bug.
That said, we really should address the default-constructibility issue. That's where std::declval comes in:
decltype(std::declval<Container&>().begin())
This will not impose any restrictions on Container, and perhaps MSVC will handle this correctly.
Related
I tried to iterate over an std::optional:
for (auto x : optionalValue)
{
...
}
With the expectation of it doing nothing if optionalValue is empty but doing one iteration if there is a value within, like it would work in Haskell (which arguably made std::optional trendy):
forM optionalValue
( \x ->
...
)
Why can't I iterate an optional? Is there another more standard C++ method to do this?
std::optional does not have a begin() and end() pair. So you cannot range-based-for over it. Instead just use an if conditional.
See Alternative 2 for the closest thing to what you want to do.
Edit: If you have a temporary from a call result you don't have to check it explicitly:
if (auto const opt = function_call()) {
do_smth(*opt);
}
The check for static_cast<bool>(opt) is done implicitly by if.
Alternative 1
Another alternative is to not use an std::optional<T> but std::variant<std::monostate, T>. You can then either use the overloaded idiom or create a custom type to handle the monostate:
template <typename F>
struct MaybeDo {
F f;
void operator()(std::monostate) const {}
template <typename T>
void operator()(T const& t) const { f(t); }
};
which will allow you to visit the value with some function:
std::variant<std::monostate, int> const opt = 7;
std::visit(MaybeDo{[](int i) { std::cout << i << "\n"; }}, opt);
Alternative 2
You can also wrap optional in a thing that allows you to iterate over it. Idea:
template <typename T>
struct IterateOpt {
std::optional<T> const& opt;
struct It {
std::optional<T> const* p;
It& operator++() {
p = nullptr;
return *this;
}
It operator++(int) {
return It{nullptr};
}
auto const& operator*() const { **p; }
};
auto begin() const {
if (opt) return It{&opt};
else end();
}
auto end() const {
return It{nullptr};
}
};
This is a crude sketch of how to do this and probably requires some love to work on different situations (like a non-const optional).
You can use this to "iterate" over the optional:
for (auto const& v: IterateOpt{function_call()}) {
do_smth(v);
)
I have the following simplified code representing a range of integers that I want to use with various std algorithms. I am trying to update my code to use C++20's ranges versions of the algorithms so I can delete all of the begin() and end() calls. In the below code, std::any_of works with my container and iterator, but std::ranges::any_of does not.
#include <iostream>
#include <algorithm>
class Number_Iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = int*;
using reference = int&;
Number_Iterator(int start) noexcept : value(start) {}
Number_Iterator& operator++() noexcept { ++value; return *this; }
bool operator==(const Number_Iterator& other) const noexcept = default;
int operator*() const noexcept { return value; }
private:
int value;
};
class Numbers {
public:
Numbers(int begin, int end) noexcept : begin_value(begin), end_value(end) {}
Number_Iterator begin() const noexcept { return {begin_value}; }
Number_Iterator end() const noexcept { return {end_value}; }
private:
int begin_value;
int end_value;
};
int main() {
const auto set = Numbers(1, 10);
const auto multiple_of_three = [](const auto n) { return n % 3 == 0; };
// Compiles and runs correctly
if(std::any_of(set.begin(), set.end(), multiple_of_three)) {
std::cout << "Contains multiple of three.\n";
}
// Does not compile
if(std::ranges::any_of(set, multiple_of_three)) {
std::cout << "Contains multiple of three.\n";
}
return 0;
}
When I try to compile the above code, I get the following error messages from Visual Studio 2019 (16.11.15) with the flag /std:c++20:
Source.cpp(42,21): error C2672: 'operator __surrogate_func': no matching overloaded function found
Source.cpp(42,7): error C7602: 'std::ranges::_Any_of_fn::operator ()': the associated constraints are not satisfied
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include\algorithm(1191): message : see declaration of 'std::ranges::_Any_of_fn::operator ()'
I have tried looking at the std::ranges::_Any_of_fn::operator() declaration, but I find myself more confused by that.
What am I missing to get the std::ranges algorithms to work with my container?
For the curious, what I'm actually iterating over are squares on a chess board, but those are represented by integers, so the difference from the above code isn't so great.
To use your range with any_of it must satisfy the input_range concept:
template< class T >
concept input_range =
ranges::range<T> && std::input_iterator<ranges::iterator_t<T>>;
Then via the input_iterator concept:
template<class I>
concept input_iterator =
std::input_or_output_iterator<I> &&
std::indirectly_readable<I> &&
requires { typename /*ITER_CONCEPT*/<I>; } &&
std::derived_from</*ITER_CONCEPT*/<I>, std::input_iterator_tag>;
and via the input_or_output_iterator concept
template <class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> /*can-reference*/;
} &&
std::weakly_incrementable<I>;
you land in the weakly_incrementable concept:
template<class I>
concept weakly_incrementable =
std::movable<I> &&
requires(I i) {
typename std::iter_difference_t<I>;
requires /*is-signed-integer-like*/<std::iter_difference_t<I>>;
{ ++i } -> std::same_as<I&>; // pre-increment
i++; // post-increment
};
in which you see that the iterator must have both the pre-increment and post-increment versions of operator++.
The iterator must also be default constructible because std::ranges::end creates a sentinel:
template< class T >
requires /* ... */
constexpr std::sentinel_for<ranges::iterator_t<T>> auto end( T&& t );
And sentinel_for
template<class S, class I>
concept sentinel_for =
std::semiregular<S> &&
std::input_or_output_iterator<I> &&
__WeaklyEqualityComparableWith<S, I>;
requires it to satisfy semiregular:
template <class T>
concept semiregular = std::copyable<T> && std::default_initializable<T>;
But without being default constructible, this substitution will fail:
template < class T >
concept default_initializable = std::constructible_from<T> && requires { T{}; } && ...
Apparently, the std::ranges algorithms require two more methods in the iterator: a default constructor and a post-increment operator (return value optional). Adding these methods allows the code to compile and run correctly:
Number_Iterator() noexcept : value(-1) {}
void operator++(int) noexcept { ++value; }
I want to pimp up my ranged based for loops, for example by enabling reversed iteraton. I managed to get it working to some degree by writing an adaptor, but I am lost on how to make the adpaptor composable.
#include <iostream>
template <typename IT> struct reversed_range {
struct reversed_iterator {
IT it;
reversed_iterator(IT it): it(it){}
reversed_iterator& operator++(){
--it;
return (*this);
}
typename std::iterator_traits<IT>::reference operator*(){
IT tmp = it;
--tmp;
return *tmp;
}
bool operator==(const reversed_iterator& other) const {
return it == other.it;
}
bool operator!=(const reversed_iterator& other) const {
return !(*this == other);
}
};
IT itbegin;
IT itend;
reversed_range(const IT& b,const IT& e): itbegin(b),itend(e){}
reversed_iterator begin() const { return reversed_iterator(itend); }
reversed_iterator end() const { return reversed_iterator(itbegin); }
};
template <typename IT>
reversed_range<IT> reverse_range(const IT& begin,const IT& end) {
return reversed_range<IT>(begin,end);
}
template <typename C>
reversed_range<C> reverse_range(const C& c) {
return reversed_range<typename C::iterator>(std::begin(c),std::end(c));
}
int main() {
int x[] = {1,2,3,4,5};
for (auto y : reverse_range(std::begin(x),std::end(x))){
std::cout << y << "\t";
}
for (auto y : reverse_range(reverse_range(std::begin(x),std::end(x)))){
std::cout << y << "\t";
}
return 0;
}
The first loop works like a charm, but for the second I get the error:
error: no type named ‘reference’ in ‘struct std::iterator_traits<reversed_range<int*> >’
typename std::iterator_traits<IT>::reference operator*(){
^~~~~~~~
I know why this happens and I know how I could fix it for this particular case. However, if i correclty understand this answer I would have to specialize iterator_traits for each possible instantiation of my reversed_iterator which isnt really feasible. I am a bit lost, most of the time I write my own iterators I find myself writing lots of boilerplate just to reach a point where I realize that I would need exponentially more boilerplate to get it working. I mean I didnt even start to consider const_iterators.
Is there a way to get the above working (whithout having to specialize iterator_traits for each iterator I ever want to reverse?
PS: tagged as C++11, because thats my current scope, but if there are improvements with respect to this, I wouldnt mind to use a newer standard.
std::iterator_traits<Iterator>::something is simply Iterator::something by default. Thus, simply add the typedefs into your reversed_iterator type:
struct reversed_iterator {
using difference_type = typename std::iterator_traits<IT>::difference_type;
using value_type = typename std::iterator_traits<IT>::value_type;
using pointer = typename std::iterator_traits<IT>::pointer;
using reference = typename std::iterator_traits<IT>::reference;
using iterator_category = /* appropriate category */;
// ...
};
I am creating a function which should take as input iterators to vector
for example:
vector<int> a;
foo(a.begin(),a.end())
The vector can hold any type.
Now the simple way to do this is using templates
template <typename Iterator>
void foo(Iterator first, Iterator last) {
for (Iterator it = first; it!=last; ++it) {
cout << *it;
}
}
I want to know if there is a way to achieve the same functionality without using templates. Since using Templates would force me to include these functions in Header file of a public API which I don't want to. So I wanted to know is there an alternate way to access the iterators without using Templates.
There are ways not to include the implementation in header files but they are not clean to implement (for instance you should know in advance the instantiations). Read here for more info about this issue:
Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?
How can I avoid linker errors with my template functions?
For instance in:
foo.h
#ifndef HI_
#define HI_
template<class Iterator>
void foo(Iterator first, Iterator last);
#endif
foo.cpp
#include "stack.h"
using namespace std;
template<class Iterator>
void foo(Iterator first, Iterator last) {
for (Iterator it = first; it != last; ++it) {
cout << *it << " ";
}
}
template
void foo( std::vector<int>::iterator first, std::vector<int>::iterator last);
template
void foo( std::vector<double>::iterator first, std::vector<double>::iterator last);
Now you can use foo function only for double and int. Other types won't link.
Hope this helps.
This is a long answer. The short answer is "type erasure"; go learn about it.
The long answer is two answers. First I cover "do you just want to be able to iterate over contiguous ints?". Then you want span. This is a really simple form of type erasure that forgets what the exact container is you are working on so long as it is contiguous and over T.
The second answer is if you actually need to deal with multiple types (not just int) and multiple kinds of containers (not just contiguous ones).
The two answers are separated by a line.
The span concept (see gsl::span) is designed for pretty much this reason. It itself is a template (over the type you are working with), but it will be a concrete instance of a template in most interfaces.
Here is a toy version of it:
template<class T>
struct span_t {
T* b = 0;
T* e = 0;
T* begin() const { return b; }
T* end() const { return e; }
span_t(span_t const&)=default;
span_t& operator=(span_t const&)=default;
span_t()=default;
span_t( T* s, T* f ):b(s),e(f) {}
span_t( T* s, std::size_t l):span_t(s, s+l){}
template<std::size_t N>
span_t( T(&arr)[N] ):span_t(arr, N) {}
std::size_t size() const { return end()-begin(); }
bool empty() const { return begin()==end(); }
T& front() const { return *begin(); }
T& back() const { return *(std::prev(end()); }
T* data() const { return begin(); }
span_t without_front( std::size_t N=1 ) const {
return {std::next( begin(), (std::min)(N, size()) ), end()};
}
span_t without_back( std::size_t N=1 ) const {
return {begin(), std::prev(end(), (std::min)(N, size()) )};
}
};
we can augment it with conversion operators
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type{};
template<class...>using void_t=void;
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
template<class C>
using dot_data_r = decltype( std::declval<C>().data() );
template<class C>
using dot_size_r = decltype( std::declval<C>().size() );
template<class C>
using can_dot_data = can_apply< dot_data_r, C >;
template<class C>
using can_dot_size = can_apply< dot_size_r, C >;
can_dot_data detects via SFINAE if .data() is valid to do on an object of type C.
Now we add a constructor:
template<class T,
std::enable_if_t<
can_dot_data<T&>{}
&& can_dot_size<T&>{}
&& !std::is_same<std::decay_t<T>, span_t>{}
, int
> =0
>
span_t( T&& t ): span_t( t.data(), t.size() ) {}
which covers std::vector and std::string and std::array.
Your function now looks like:
void foo(span_t<int> s) {
for (auto&& e:s)
std::cout << s;
}
}
with use:
std::vector<int> a;
foo(a);
now, this only works for contiguous containers of a specific type.
Suppose this is not what you want. Maybe you do need to solve this for a myriad of types, and you don't want to expose everything in the header.
Then what you need to do is known as type erasure.
You need to work out what minimal set of operations you need from the provided types. Then you need to write wrappers that "type erase" these operations down to "typeless" operations.
This goes in the header, or in another helper header.
In the interface of the function, or in a header intermediate helper, you take the incoming types and do the type erasure, then pass the type-erased types into the "real" implementation.
An example of type erasure is std::function. It takes almost anything that can be invoked with a fixed signature, and turns it into a single type-erased type. Everything except how to copy, destroy and invoke an instance of the type is "forgotten" or erased.
For your case:
template <typename Iterator>
void foo(Iterator first, Iterator last) {
for (Iterator it = first; it!=last; ++it) {
cout << *it;
}
}
I see two things that need to be erased down to; iteration, and printing.
struct printable_view_t {
void const* data = 0;
void(*print_f)(std::ostream& os, void const*) = 0;
explicit operator bool()const{return data;}
printable_view_t() = default;
printable_view_t(printable_view_t const&) = default;
template<class T,
std::enable_if_t<!std::is_same<T, printable_view_t>{}, int> =0
>
printable_view_t( T const& t ):
data( std::addressof(t) ),
print_f([](std::ostream& os, void const* pv){
auto* pt = static_cast<T const*>(pv);
os << *pt;
})
{}
std::ostream& operator()(std::ostream& os)const {
print_f(os, data);
return os;
}
friend std::ostream& operator<<(std::ostream& os, printable_view_t p) {
return p(os);
}
};
printable_view_t is an example of type-erasing "I can be printed".
void bar( printable_view_t p ) {
std::cout << p;
}
void test_bar() {
bar(7);
bar(3.14);
bar(std::string("hello world"));
}
The next thing we'd have to do is type erase iteration. This is harder, because we want to type erase iteration over iterating over a printable_view_t type.
Type erasing foreach is a tad easier, and often more efficient.
template<class View>
struct foreach_view_t {
void* data = 0;
void(*func)( std::function<void(View)>, void* ) = 0;
explicit operator bool()const{return data;}
foreach_view_t() = default;
foreach_view_t(foreach_view_t const&) = default;
template<class T,
std::enable_if_t<!std::is_same<std::decay_t<T>, foreach_view_t>{}, int> =0
>
foreach_view_t( T&& t ):
data( const_cast<std::decay_t<T>*>(std::addressof(t)) ),
func([](std::function<void(View)> f, void* pv){
auto* pt = static_cast<std::remove_reference_t<T>*>(pv);
for (auto&& e : *pt)
f(decltype(e)(e));
})
{}
void operator()(std::function<void(View)> f)const{
func(f, data);
}
};
we then daisy chain these together
void foo(foreach_view_t<printable_view_t> x) {
x([](auto p){ std::cout << p; });
}
test code:
std::vector<int> a{1,2,3};
foo(a);
Now much of the header code was "hoisted" into the type erasure types instead of a function template body. But careful choice of the points of type erasure can let you keep what you need from the types precise and narrow, and the logic of how you use those operations private.
As an example, the above code doesn't care where you are printing it to; std::cout was not part of the type erasure.
Live example.
I want to know if there is a way to achieve the same functionality without using templates. [...] I wanted to know is there an alternate way to access the iterators without using Templates.
Yes, if you use C++14, but...
Since using Templates would force me to include these functions in Header file of a public API which I don't want to.
... isn't a useful way for you because it's equivalent to use templates and you have to put it in the header file.
In C++14 you can use a lambda function with auto parameters.
auto foo = [](auto first, auto last)
{ for (auto it = first ; it != last; ++it ) std::cout << *it; };
The autos aren't template (from a formal point of view) but are equivalent and you can't declare foo in the header and develop it in a cpp file.
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.