How to Make My Custom Type to Work With "Range-Based For Loops"

How to make my custom type to work with range-based for loops?

The standard has been changed since the question (and most answers) were posted in the resolution of this defect report.

The way to make a for(:) loop work on your type X is now one of two ways:

  • Create member X::begin() and X::end() that return something that acts like an iterator

  • Create a free function begin(X&) and end(X&) that return something that acts like an iterator, in the same namespace as your type X

And similar for const variations. This will work both on compilers that implement the defect report changes, and compilers that do not.

The objects returned do not have to actually be iterators. The for(:) loop, unlike most parts of the C++ standard, is specified to expand to something equivalent to:

for( range_declaration : range_expression )

becomes:

{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}

where the variables beginning with __ are for exposition only, and begin_expr and end_expr is the magic that calls begin/end

The requirements on the begin/end return value are simple: You must overload pre-++, ensure the initialization expressions are valid, binary != that can be used in a boolean context, unary * that returns something you can assign-initialize range_declaration with, and expose a public destructor.

Doing so in a way that isn't compatible with an iterator is probably a bad idea, as future iterations of C++ might be relatively cavalier about breaking your code if you do.

As an aside, it is reasonably likely that a future revision of the standard will permit end_expr to return a different type than begin_expr. This is useful in that it permits "lazy-end" evaluation (like detecting null-termination) that is easy to optimize to be as efficient as a hand-written C loop, and other similar advantages.


¹ Note that for(:) loops store any temporary in an auto&& variable, and pass it to you as an lvalue. You cannot detect if you are iterating over a temporary (or other rvalue); such an overload will not be called by a for(:) loop. See [stmt.ranged] 1.2-1.3 from n4527.

² Either call the begin/end method, or ADL-only lookup of free function begin/end, or magic for C-style array support. Note that std::begin is not called unless range_expression returns an object of type in namespace std or dependent on same.


In c++17 the range-for expression has been updated

{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr;
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}

with the types of __begin and __end have been decoupled.

This permits the end iterator to not be the same type as begin. Your end iterator type can be a "sentinel" which only supports != with the begin iterator type.

A practical example of why this is useful is that your end iterator can read "check your char* to see if it points to '0'" when == with a char*. This allows a C++ range-for expression to generate optimal code when iterating over a null-terminated char* buffer.

struct null_sentinal_t {
template<class Rhs,
std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
>
friend bool operator==(Rhs const& ptr, null_sentinal_t) {
return !*ptr;
}
template<class Rhs,
std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
>
friend bool operator!=(Rhs const& ptr, null_sentinal_t) {
return !(ptr==null_sentinal_t{});
}
template<class Lhs,
std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
>
friend bool operator==(null_sentinal_t, Lhs const& ptr) {
return !*ptr;
}
template<class Lhs,
std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
>
friend bool operator!=(null_sentinal_t, Lhs const& ptr) {
return !(null_sentinal_t{}==ptr);
}
friend bool operator==(null_sentinal_t, null_sentinal_t) {
return true;
}
friend bool operator!=(null_sentinal_t, null_sentinal_t) {
return false;
}
};

live example of this.

Minimal test code is:

struct cstring {
const char* ptr = 0;
const char* begin() const { return ptr?ptr:""; }// return empty string if we are null
null_sentinal_t end() const { return {}; }
};

cstring str{"abc"};
for (char c : str) {
std::cout << c;
}
std::cout << "\n";

Here is a simple example.

namespace library_ns {
struct some_struct_you_do_not_control {
std::vector<int> data;
};
}

Your code:

namespace library_ns {
int* begin(some_struct_you_do_not_control& x){ return x.data.data(); }
int* end(some_struct_you_do_not_control& x){ return x.data.data()+x.data.size(); }
int const* cbegin(some_struct_you_do_not_control const& x){ return x.data.data(); }
int* cend(some_struct_you_do_not_control const& x){ return x.data.data()+x.data.size(); }
int const* begin(some_struct_you_do_not_control const& x){ return cbegin(x); }
int const* end(some_struct_you_do_not_control const& x){ return cend(x); }
}

this is an example how you can augment a type you don't control to be iterable.

Here I return pointers-as-iterators, hiding the fact I have a vector under the hood.

For a type you do own, you can add methods:

struct egg {};
struct egg_carton {
auto begin() { return eggs.begin(); }
auto end() { return eggs.end(); }
auto cbegin() const { return eggs.begin(); }
auto cend() const { return eggs.end(); }
auto begin() const { return eggs.begin(); }
auto end() const { return eggs.end(); }
private:
std::vector<egg> eggs;
};

here I reuse the vector's iterators. I use auto for brevity; in c++11 I'd have to be more verbose.

Here is a quick and dirty iterable range-view:

template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
std::size_t size() const { return end()-begin(); } // do not use distance: O(n) size() is toxic
bool empty() const { return begin()==end(); }

range_t without_back() const {
if(emptty()) return *this;
return {begin(), std::prev(end())};
}
range_t without_back( std::size_t n ) const {
auto r=*this;
while(n-->0 && !r.empty())
r=r.without_back();
return r;
}
range_t without_front() const {
if(empty()) return *this;
return {std::next(begin()), end()};
}
range_t without_front( std::size_t n ) const {
auto r=*this;
while(n-->0 && !r.empty())
r=r.without_front();
return r;
}
decltype(auto) front() const { return *begin(); }
decltype(auto) back() const { return *(std::prev(end())); }
};
template<class It>
range_t(It,It)->range_t<It>;
template<class C>
auto make_range( C&& c ) {
using std::begin; using std::end;
return range_t{ begin(c), end(c) };
}

using c++17 template class deduction.

std::vector<int> v{1,2,3,4,5};
for (auto x : make_range(v).without_front(2) ) {
std::cout << x << "\n";
}

prints 3 4 5, skipping first 2.

c++ Range_Based Loop with custom Object [duplicate]

The range-for protocol requires that begin() and end() return a type that conforms to the standard library's Iterator concept. This includes things like being able to dereference the iterator with operator*, and increment it with operator++.

An object reference, as you are returning from your begin(), does not meet these requirements, which is what the compiler is complaining about.

Writing your own iterator is quite tedious, especially if you want to support all the random-access facilities that std::vector's iterators offer. There are libraries that can make this a bit easier though.

A far more straightforward way to do things would be to simply forward the iterators that your list member already gives, i.e.

class List
{
public:
using iterator = std::vector<shared_ptr<Object>>::iterator;
using const_iterator = std::vector<shared_ptr<Object>>::const_iterator;

std::vector<std::shared_ptr<Object>> list;

iterator begin() { return list.begin(); }
const_iterator begin() const { return list.begin(); }
iterator end() { return list.end(); }
const_iterator end() const { return list.end(); }
};

Now you can use a List in a range-for loop by saying

for (auto& optr : list) {
do_something_with_object(*optr);
}

The only catch is that dereferencing the iterator will give you a reference to a shared_ptr<Object>, not an Object&, so you'll need to dereference this again to actually use the object itself (as above).

Custom class range based for-loop over a 2D map

Yes, It can be done. You must introduce iterator functionality.

If you look in the CPP reference here, then you can see the requirements for the range based for loop in the explanation. Only 5 functions are needed.

  • Your class must have a begin() function that returns a custom iterator

  • Your class must have a end() function that returns a custom iterator

  • Your class must have a custom iterator and a function for dereferencing it

  • Your class must have a custom iterator and a function for incrementing it

  • Your class must have a custom iterator and a function for comparison

The implementation of a custom iterator is very simple.

You write 5 using statements to fulfill formal requirements. Then, you need to define the internal representation of your iterator, for example a pointer or a other iterator.

In the example below, we reuse the 2 iterators from the nested maps. An iterator to the outer map and an interator for the inner map. For easier implementation of functionality, we will also store a pointer to the surrounding custom class.

The increment (and decrement operation) causes a little bit more effort, because we need to handle the wrap around of the inner map iterator.

Example: If the inner iterator reaches the end, we must increment the outer iterator and then reset the inner iterator to the begin position.

I adedd also a decrement function which faces similar challenges.

By adding a difference function, I make the whole thing even sortable, by destroying the relation of the strings to the double. So, do not do that.

But with that function, we can easily implement space ship like comparision of iterators.

Please see one of many potential solutions below. I added some more functions for your convenience.

#include<string> 
#include<map>
#include<iostream>
#include <iterator>
#include <algorithm>
#include <numeric>

using Map = std::map<std::string, double>;
using MapMap = std::map<std::string, Map>;

struct MyClass {
struct iterator; // Forward declaration

MapMap mp{};

bool empty() { return mp.empty(); }
size_t size() { return std::accumulate(mp.begin(), mp.end(), 0u, [](const size_t& sum, const auto& m) { return sum + m.second.size(); }); }

iterator begin() { return iterator(&mp, mp.begin()->second.begin(), mp.begin()); }
iterator end() { return iterator(&mp, ((--mp.end())->second).end(), mp.end()); }

// iterator stuff ---------------------------------------------------------------------------
struct iterator {
// Definitions ----------------
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = double;
using pointer = double*;
using reference = double&;

// Data
MapMap* outerMapPtr{}; // Reference to surrounding nested map
MapMap::iterator outerMapIterator{}; // Iterator to the outer part of the nesed map
Map::iterator innerMapIterator{}; // Iterator to the outer part of the nesed map

// Constructor for iterator. Take a pointer to the surrounding nested map and both iterators
iterator(MapMap* const mmSD, const Map::iterator& msdIter, const MapMap::iterator& mmsdIter) :
outerMapPtr(mmSD), innerMapIterator(msdIter), outerMapIterator(mmsdIter) {}

// Dereferencing
reference operator *() const { return innerMapIterator->second; }
reference operator->() const { return **this; }

// Comparison. Must be template, because the other iterator may be of different type
template <typename Iter>
bool operator != (const Iter& other) const { return(outerMapIterator != other.outerMapIterator) or (innerMapIterator != other.innerMapIterator); }
template <typename Iter>
bool operator == (const Iter& other) const { return(outerMapIterator == other.outerMapIterator) and (innerMapIterator == other.innerMapIterator); }

bool operator < (const iterator& other) const { return other - *this < 0; };
bool operator <= (const iterator& other) const { return other - *this <= 0; };
bool operator > (const iterator& other) const { return other - *this > 0; };
bool operator >= (const iterator& other) const { return other - *this >= 0; };

// Arithmetic operations
// A little bit complex
iterator operator ++() {
// If we are at the end with the outer iterator, we do nothing
if (outerMapPtr->empty() or (outerMapIterator != outerMapPtr->end())) {

// We want to increment the inner iterator. Before we do that, we check, if this is already at the end
if (innerMapIterator != outerMapIterator->second.end())
++innerMapIterator;

// So, now the innerMapIterator can be at the end, either from the beginning or after incrementing it
if (innerMapIterator == outerMapIterator->second.end()) {

// Increment outer iterator
++outerMapIterator;

// And reset the inner interator back to begin, but only if the outer iterator is not at the end now
if (outerMapIterator != outerMapPtr->end())
innerMapIterator = outerMapIterator->second.begin();
}
}
return *this;
}
iterator operator --() {
// No decrementation on empty container
if (not outerMapPtr->empty()) {

// If we are at the end of the outer iterator then decrement it
if (outerMapIterator == outerMapPtr->end())
--outerMapIterator;

// If we are not at the begin the inner iterator
if (innerMapIterator != outerMapIterator->second.begin())
--innerMapIterator;
else {
// Inner iterator was at begin, therefore also decrement outer one
if (outerMapIterator != outerMapPtr->begin()) {
--outerMapIterator;
innerMapIterator = outerMapIterator->second.end();
if (innerMapIterator != outerMapIterator->second.begin())
--innerMapIterator;
}
}
}
return *this;
}

// Derived functions
iterator operator++(int) { iterator tmp = *this; ++* this; return tmp; }
iterator operator--(int) { iterator tmp = *this; --* this; return tmp; }
iterator operator +(const difference_type& n) const {
iterator temp{ *this }; difference_type k{ n }; if (k > 0) while (k--)++temp; else while (k++)--temp; return temp;
}
iterator operator +=(const difference_type& n) {
difference_type k{ n }; if (k > 0) while (k--)++* this; else while (k++)--* this; return *this;
};
iterator operator -(const difference_type& n) const {
iterator temp{ *this }; difference_type k{ n }; if (k > 0) while (k--)--temp; else while (k++)++temp; return temp;
}
iterator operator -=(const difference_type& n) {
difference_type k{ n }; if (k > 0) while (k--)--* this; else while (k++)++* this; return *this;
};

// Difference. Very inefficient
difference_type operator-(const iterator& other) const {

difference_type result{};
// No subtraction on empty container
if (not outerMapPtr->empty()) {

int indexThis{ }, indexOther{ }, index{};

iterator current(outerMapPtr, outerMapPtr->begin()->second.begin(), outerMapPtr->begin());
iterator last(outerMapPtr, ((--outerMapPtr->end())->second).end(), outerMapPtr->end());

for (; current != last; ++current, ++index) {
if (current == *this)
indexThis = index;
if (current == other)
indexOther = index;
}
if (current == *this)
indexThis = index;
if (current == other)
indexOther = index;

if (indexThis >= 0 and indexOther >= 0)
result = indexThis - indexOther;
}
return result;
}
};
};

int main()
{
MyClass mycls;
mycls.mp["a"]["a"] = 1;
mycls.mp["a"]["b"] = 2;
mycls.mp["a"]["c"] = 3;
mycls.mp["b"]["a"] = 4;
mycls.mp["b"]["b"] = 5;
mycls.mp["b"]["c"] = 6;
mycls.mp["d"]["a"] = 7;
mycls.mp["d"]["b"] = 8;
mycls.mp["d"]["c"] = 9;
mycls.mp["e"]["a"] = 10;
mycls.mp["e"]["b"] = 11;
mycls.mp["e"]["c"] = 12;

std::cout << "\nSize: " << mycls.size() << "\n\n";

for (double d : mycls)
std::cout << d << ' ';

return 0;
}

Custom container traversal with range-based for loop

Yes, you need to implement some form of iterator and override std::begin(container) and std::end(container) (might work also if you container has begin and end methods).

Internally the code is equivalent to something like the following (this is just to get the point across, the compiler can write it slightly differently, see here for more details).

auto _end = end(v);
for (auto _it = begin(v); _it != _end; ++_it) {
auto c = *_it;
<the rest of loop code>
}

So if your iterator and overrides work as expected it will work for the for loop as well.

How to allow range-for loop on my class? [duplicate]

The loop is defined to be equivalent to:

for ( auto __begin = <begin-expr>,
__end = <end-expr>;
__begin != __end;
++__begin ) {
auto& f = *__begin;
// loop body
}

where <begin-expr> is foo.begin(), or begin(foo) if there isn't a suitable member function, and likewise for <end-expr>. (This is a simplification of the specification in C++11 6.5.4, for this particular case where the range is a lvalue of class type).

So you need to define an iterator type that supports pre-increment ++it, dereference *it and comparison i1 != i2; and either

  • give foo public member functions begin() and end(); or
  • define non-member functions begin(foo) and end(foo), in the same namespace as foo so that they can be found by argument-dependent lookup.

Range based for in C++17 for custom container or general classes with different begin/end types

The main problem with your solution is that you try to so things in a non standard way which has many limitations and might make it hard to use by expert who have a good understanding how iterators works in STL.

I will try to answer most questions in a less technical way that should help understand how it works in practice.

1) Sharing the type will lead to some problem as some algorithms might need more than one active iterator. Also, it does not respect the SRP (single responsibility principle) and thus is not a good design practice.

2) It only need to return something that essentially behave like an iterator.

3) Or it could be a pointer if the data is contiguous in memory.

4) Usually the begin function returns an iterator by value.

5) Usually the end function returns an iterator to the end. It could also returns a sentinel object if the end is not really a position (for ex. input stream or if the last value of the container is a sentry).

6) You could put your own typedef/aliases inside the class and use them as appropriate.

7) The return value type of operator * will almost always be different than the one of the iterator type.

Then some remarks/suggestions

  • In you case LinkedList would be the iterator type (or you would use a wrapper around that).
  • Often you want you container to be more than an iterator if you want to be able to know the size for example without having to iterate the whole list. Also the container might provide some optimized member function like sort in std::list.
  • STL source code might be a good source if you want to know how expert do it.
  • Your code does not respect constness and thus would not work if you replace Example example; with const Example example;.
  • Most of your operators does not follows usual convention in their declaration.
  • Your begin function has side effect.
  • With your code, you won't be able to store an iterator to a position in your list. This mean that algorithm like sorting or erasing matching items will probably fails.
  • Putting void inside empty parameter list is essentially an obsolete way to write code that should not be used anymore in C++. It only has useful purpose in C.
  • Operator++ should usually return a reference to this.
  • If you want to use a sentry for the end value, it is better to use your own type (an enum value could do). Otherwise, it would allows unexpected comparison to compile like example != 25 (and in that case, one might thing that the loop would end when the value of k is 25) so it make the code harder to understand.
  • Why not use std::forward_list instead of reinventing the wheel.
  • If you really need to use your LinkedList then STL one could be a valuable source of information on how to properly define iterators.

C++ Iterator for C Linked Lists: to use range-based for loops

Iterators are pseudo-pointer types. That means they themselves are regular.

struct Iterator {
LinkedList *current;
LinkedList &c;
};

Here you mix references and pointers. This is a serious anti-pattern, as what does assignment do? There is no sensible answer.

I would remove the c member entirely.

Next you need to broadcast an iterator type. Yours looks like a forward iterator. All end iterators can be equal.

Iterator begin(LinkedList *c) { return Iterator {c, *c}; }
Iterator end(LinkedList *c) { return Iterator {nullptr, *c}; }

These look ok. Just remove *c.

Note that the name does not have to be Iterator. begin/end must be defined in the namespace of LinkedList, but the return type does not have to be.

Iterator &operator++(Iterator &p) { p.current = p.current->next; return p; }

I usually implement this as a member function, and implement both pre and post increment; post is implemented using pre and copy.

LinkedList *operator*(Iterator p) { return p.current; }

This is wrong. It should return *p.current as a double&.

bool operator!=(Iterator lhs, Iterator rhs) { return (lhs.current != rhs.current); }

sure. Also implement == as !(lhs!=rhs).

Look up the forward iterator concept and forward iterator tag. Include the types needed for std::iterator_traits.

For other things to iterate, give the iterator a different name. This can be via a different namespace.

If the thing that differs is just the type of the value, you can make it a template pretty easy. Then you only have to manually write begin/end.

If the name of v also changes, you could use ADL on a GetValue(List*) function you write as a customization point.


Now, being usable in a ranged based for is different than being an iterator. Ranged based for is a tad easier; but the above upgrades you to a full forward iterator, which in turn reduces surprise when you try to use a std algorithm or basically anything else.


How I would write it:

// Iteration::LinkedListIterator<X> assumes that X is a linked list node
// with members ->next and ->value. If it isn't, override the customization
// points GetNextNode and GetListElement in the namespace of X.

namespace Iteration {
template<class List>
List* GetNextNode( List* l ) {
if (!l) return l;
return l->next;
}
template<class List>
decltype(auto) GetListElement( List* l ) {
return l->value;
}
template<class List>
struct LinkedListIterator {

using self=LinkedListIterator;
List *current;
self& operator++(){ current = GetNextNode(current); return *this; }
self operator++(int)&{ auto copy = *this; ++*this; return copy; }
decltype(auto) operator*() {
return GetListElement(current);
}
decltype(auto) operator*() const {
return GetListElement(current);
}
auto operator->() {
return std::addressof(GetListElement(current));
}
auto operator->() const {
return std::addressof(GetListElement(current));
}
friend bool operator==(self const& lhs, self const& rhs) {
return lhs.current == rhs.current;
}
friend bool operator!=(self const& lhs, self const& rhs) {
return lhs.current != rhs.current;
}

using iterator_category = std::forward_iterator_tag;
using value_type = std::decay_t<decltype(GetListElement(std::declval<List*>()))>;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
};
};

struct LinkedList {
double v;
LinkedList *next;
};

// customization point; the name of
double& GetListElement( LinkedList* l ) { return l->v; }
double const& GetListElement( LinkedList const* l ) { return l->v; }
Iteration::LinkedListIterator<LinkedList> begin( LinkedList* l ) {
return {l};
}
Iteration::LinkedListIterator<LinkedList> end( LinkedList* l ) {
return {nullptr};
}

How do I implement an iterable structure?

I think the type you're looking for is std::vector<combo>::iterator.

Example:

typedef std::pair<std::string, std::string> combo;
struct MATCH {
std::vector<combo> matches;
std::vector<combo>::iterator begin() { return matches.begin(); }
std::vector<combo>::iterator end() { return matches.end(); }
};


int main()
{
MATCH m = { { {"something", "something"} } };
for (const combo& i : m)
cout << i.first << " " << i.second << std::endl;

return 0;
}


Related Topics



Leave a reply



Submit