I'm trying to improve my C++ skills by porting the major examples in Algorithms, 4th Edition by Sedgewick and Wayne. I wrote a generic stack implementation based on their Java example.
My stack works fine, but I'd like to make a performance improvement and got stuck trying to write a reverse iterator.
template<typename T> class ResizingArrayStack {
public:
T* begin() { return &array_ptr[0]; }
T* end() { return &array_ptr[N]; }
...
// Here we're iterating forward through the array, with an unused variable `i`.
// It would be nice performance-wise to iterate in reverse without calling pop(), and without triggering a resize.
for ( auto& i : lifo_stack ) {
cout << "Current loop iteration has i = " << i << endl;
}
// // Alternatively, pop from the stack N times.
// cout << "Popped an item from the stack: " << lifo_stack.pop() << endl;
I tried switching the begin and end member functions above, but found that the expanded for-loop always increments with ++__begin, even if __end is at a lower memory address. How can we get i to loop in reverse (LIFO with respect to the stack)?
Please feel free to comment on my code style if there are egregious errors or aspects that look out of date. I want stay in-line with good 'modern' C++.
If you want to use the range-for loop with reverse iterators, you can use a wrapper class Reverse that stores a range and returns the reverse_iterators corresponding to begin and end
#include <iostream>
#include <iterator>
#include <vector>
template<class Rng>
class Reverse
{
Rng const& rng;
public:
Reverse(Rng const& r) noexcept
:
rng(r)
{}
auto begin() const noexcept { using std::end; return std::make_reverse_iterator(end(rng)); }
auto end() const noexcept { using std::begin; return std::make_reverse_iterator(begin(rng)); }
};
int main()
{
std::vector<int> my_stack;
my_stack.push_back(1);
my_stack.push_back(2);
my_stack.push_back(3);
// prints 3,2,1
for (auto const& elem : Reverse(my_stack)) {
std::cout << elem << ',';
}
}
Live Example
Note that this uses C++1z template deduction, only supported by g++ 7.0 SVN and clang 5.0 SVN. For earlier compilers you could add a helper function
template<class Rng>
auto MakeReverse(Rng const& rng) { return Reverse<Rng>(rng); }
for (auto const& elem : MakeReverse(my_stack)) {
std::cout << elem << ',';
}
Live Example (works as of gcc 5.1 or clang 3.5)
Alternatively, you can use the Boost.Range library and simply do (will work any C++11 compiler)
#include <iostream>
#include <vector>
#include <boost/range/adaptor/reversed.hpp>
int main()
{
std::vector<int> my_stack;
my_stack.push_back(1);
my_stack.push_back(2);
my_stack.push_back(3);
for (auto const& elem : boost::adaptors::reverse(my_stack)) {
std::cout << elem << ',';
}
}
Live Example
Note that you have to be careful about passing temporary variables to such adaptors, both mine and the Boost adaptor do not work when passing e.g. a raw std::vector<int>{3,2,1}, as pointed out by #Pixelchemist in the comments.
Here a scratch for your problem. Don't consider this as working code. Use it to just get an idea of how reverse iterator MAY be implemented (just one many possible ways).
template<typename T> class ResizingArrayStack {
public:
class reverse_iterator
{
ResizingArrayStack & _storage;
int _pointer;
public:
inline reverse_iterator(ResizingArrayStack & storage,
int pointer)
: _storage(storage)
, _pointer(pointer)
{}
inline reverse_iterator & operator++() // prefix
{
--_pointer;
return *this;
}
inline reverse_iterator operator++() // postfix
{
reverse_iterator tmp(*this);
--_pointer;
return tmp;
}
inline T & operator*()
{
return _storage.getByIndex(_pointer);
}
// TODO: == != etc
};
reverse_iterator rbegin() { return reverse_iterator(*this, N - 1); }
reverse_iterator rend() { return reverse_iterator(*this, -1); }
// ... //
};
once you have functioning (regular) iterators,
implement reverse iterators using the standard library
helper class template std::reverse_iterator
#include <iterator>
class XX {
// your code
typedef std::reverse_iterator<iterator> reverse_iterator;
reverse_iterator rbegin() { return reverse_iterator{end()}; }
reverse_iterator rend() { return reverse_iterator{begin()}; }
Looking at your full codelifo_stack.pop() invalidates your iterators, so it cannot be used inside a ranged for. You have Undefined Behavior
Moreover it doesn't make much sense to use a range for for a stack. If you can iterate over its elements then it's not a stack now isn't it? A stack has the property that you can only access the most recent inserted element.
Based on your comment:
Consider the case where you add items slowly and individually, but
wish to dump them out of the stack as quickly as possible. I don't
want the overhead of copying and resizing arrays which pop() would
trigger at that moment.
I still think that ranged-for does not make sense for a stack.
Here is how I see your problem solved:
lifo_stack.disable_resizing(); // prevents resizing
while (!lifo_stack.is_empty()
{
lifo_stack.pop(); // maybe use the poped element
}
lifo_stack.enable_resizing(); // re-enables resizing and triggers a resize
If you don't need the popped elements and just want to emtpy the stack, there is a faster way (based on your class implementation):
// empties the stack
void clear()
{
delete[] array_ptr;
array_ptr = new T[1];;
max_size = 1;
N = 0;
}
One last final though if you want to use modern C++ then use unique_ptr instead of manual new and delete. It is easier but most importantly safer. And read on the rule of 0/3/5.
This solution does not introduce unnecessary copies and does not exhibit incorrect forwarding as suggested by some comments. Explanation below.
You can use some wrapper which has begin and end functions that actually
return reverse iterators.
template<class T>
struct revert_wrapper
{
T o;
revert_wrapper(T&& i) : o(std::forward<T>(i)) {}
};
template<class T>
auto begin(revert_wrapper<T>& r)
{
using std::end;
return std::make_reverse_iterator(end(r.o));
}
template<class T>
auto end(revert_wrapper<T>& r)
{
using std::begin;
return std::make_reverse_iterator(begin(r.o));
}
template<class T>
auto begin(revert_wrapper<T> const& r)
{
using std::end;
return std::make_reverse_iterator(end(r.o));
}
template<class T>
auto end(revert_wrapper<T> const& r)
{
using std::begin;
return std::make_reverse_iterator(begin(r.o));
}
template<class T>
auto reverse(T&& ob)
{
return revert_wrapper<T>{ std::forward<T>(ob) };
}
Used like this:
std::vector<int> v{1, 2, 3, 4};
for (auto i : reverse(v))
{
std::cout << i << "\n";
}
or in your case
for ( auto& i : reverse(lifo_stack) ) {
cout << "Current loop iteration has i = " << i << endl;
cout << "Popped an item from the stack: " << lifo_stack.pop() << endl;
}
Since fowarding is not an easy topic and there is misconception around I'll further explain some details. I'll use std::vector<int> as an example for the "to be reversed" type T.
1. The function template reverse.
1.1 Passing an lvalue std::vector<int>:
std::vector<int> v{1, 2, 3, 4};
auto&& x = reverse(v);
The compiler created instance of reverse in this case would look like:
template<>
auto reverse<std::vector<int>&>(std::vector<int>& ob)
{
return revert_wrapper<std::vector<int>&>{ std::forward<std::vector<int>&>(ob) };
}
We see two things here:
The T of revert_wrapper will be std::vector<int>&, so no copy involved.
we're forwarding an lvalue as an lvalue to the constructor of revert_wrapper
1.2 Passing an rvalue std::vector<int>
std::vector<int> foo();
auto&& x = reverse(foo());
We look again at the instantiation of the function template:
template<>
auto reverse<std::vector<int>>(std::vector<int>&& ob)
{
return revert_wrapper<std::vector<int>>{ std::forward<std::vector<int>>(ob) };
}
And can again note two things:
The T of revert_wrapper will be std::vector<int>, thus copy the vector, preventing the rvalue from going out of scope before any range based loop can run
an rvalue std::vector<int>&& will be forwarded to the constructor of revert_wrapper
2. The class template revert_wrapper and its constructor
2.1 The revert_wrapper created by reverse in case of an lvalue std::vector<int>&
template<>
struct revert_wrapper<std::vector<int>&>
{
std::vector<int>& o;
revert_wrapper(std::vector<int>& i) :
o(std::forward<std::vector<int>&>(i)) {}
};
As noted above: No copies involved as we store a reference.
The forward also seems familiar and indeed it is just the same as above within reverse: We forward an lvalue as lvalue reference.
2.2 The revert_wrapper created by reverse in case of an rvalue std::vector<int>&&
template<>
struct revert_wrapper<std::vector<int>>
{
std::vector<int> o;
revert_wrapper(std::vector<int>&& i) :
o(std::forward<std::vector<int>>(i)) {}
};
This time we have the object stored by value to prevent a dangling reference.
Also the forwarding is fine: We forwarded the rvalue reference from reverse to the revert_wrapper constructor and we forward it on to the std::vector constructor. We could've used static_cast<T&&>(i) in the same way but we're not (std::)mov(e)ing from an lvalue, we're forwarding:
lvalues as lvalues and
rvalues as rvalues.
We can also see one more thing here:
The only available constructor of the revert_wrapper instance that stores by value takes an rvalue. Therefore, we can't (easily) trick this class to make unnecessary copies.
Note that replacing std::forward with std::move inside the initializer of o in the revert_wrapper constructor would actually be wrong.
Please see an excellent answer from TemplateRex here. I was able to solve the problem without a wrapper class, so I'll give a shot at answering my own question.
Here is the most helpful example I found on implementing iterators at http://en.cppreference.com, and you can find my updated ResizingArrayStack code at the same GitHub link as found the question.
template<typename T> class ResizingArrayStack {
public:
//----- Begin reversed iteration section -----//
// Please see the example here, (http://en.cppreference.com/w/cpp/iterator/iterator).
// Member typedefs inherit from std::iterator.
class stackIterator: public std::iterator<
std::input_iterator_tag, // iterator_category
T, // value_type
T, // difference_type
const T*, // pointer
T // reference
>{
int index = 0;
T* it_ptr = nullptr;
public:
// Prefix ++, equal, unequal, and dereference operators are the minimum required for range based for-loops.
stackIterator(int _index = 0, T* _it_ptr = nullptr) { index = _index; it_ptr = _it_ptr; }
// Here is where we reverse the sequence.
stackIterator& operator++() { --index; return *this; }
bool operator==(stackIterator other) { return index == other.index; }
bool operator!=(stackIterator other) { return !( *this == other ); }
T operator*() { return it_ptr[index-1]; }
};
stackIterator begin() { return stackIterator(N, array_ptr); }
stackIterator end() {
N = 0; // 'Empty' the array.
max_size = 1; // Don't waste time calling resize() now.
return stackIterator(0, array_ptr);
}
//----- End reversed iteration section -----//
private:
// Allocate space for a traditional array on the heap.
T* array_ptr = new T[1];
// Keep track of the space allocated for the array, max_size * sizeof(T).
int max_size = 1;
// Keep track of the current number of items on the stack.
int N = 0;
Calling code where the range based for-loop iterates in reversed (or LIFO) order by default.
// It's nice performance-wise to iterate in reverse without calling pop() or triggering a resize.
for ( auto i : lifo_stack) {
cout << "Current loop iteration has i = " << i << endl;
}
Related
This is a code example using std::reverse_iterator:
template<typename T, size_t SIZE>
class Stack {
T arr[SIZE];
size_t pos = 0;
public:
T pop() {
return arr[--pos];
}
Stack& push(const T& t) {
arr[pos++] = t;
return *this;
}
auto begin() {
return std::reverse_iterator(arr+pos);
}
auto end() {
return std::reverse_iterator(arr);
// ^ does reverse_iterator take this `one back`? how?
}
};
int main() {
Stack<int, 4> s;
s.push(5).push(15).push(25).push(35);
for(int val: s) {
std::cout << val << ' ';
}
}
// output is as expected: 35 25 15 5
When using std::reverse_iterator as an adaptor for another iterator, the newly adapted end shall be one before the original begin. However calling std::prev on begin is UB.
How does std::reverse_iterator hold one before begin?
Initialization of std::reverse_iterator from an iterator does not decrease the iterator upon initialization, as it would then be UB when sending begin to it (one cannot assume that std::prev(begin) is a valid call).
The trick is simple, std::reverse_iterator holds the original iterator passed to it, without modifying it. Only when it is being dereferenced it peeks back to the actual value. So in a way the iterator is pointing inside to the next element, from which it can get the current.
It would look something like:
// partial possible implementation of reverse_iterator for demo purpose
template<typename Itr>
class reverse_iterator {
Itr itr;
public:
constexpr explicit reverse_iterator(Itr itr): itr(itr) {}
constexpr auto& operator*() {
return *std::prev(itr); // <== only here we peek back
}
constexpr auto& operator++() {
--itr;
return *this;
}
friend bool operator!=(reverse_iterator<Itr> a, reverse_iterator<Itr> b) {
return a.itr != b.itr;
}
};
This is however an internal implementation detail (and can be in fact implemented in other similar manners). The user of std::reverse_iterator shall not be concerned with how it is implemented.
I have classes that behave like a lazy container, generating values on the fly. Then in some cases, I would like to filter the values. Boost::range::adaptors::filtered seems to be well-suited. However it does not keep any reference to the "range", it just store the begin/end iterators.
The following code mimics my use case. But it does not work: the container is destroyed before r is used.
#include <iostream>
#include <vector>
#include <boost/range/adaptor/filtered.hpp>
#define PING() std::cerr << __PRETTY_FUNCTION__ << '\n'
using ints = std::vector<int>;
struct container
{
container() { PING(); }
~container() { PING(); }
using value_type = typename ints::value_type;
using iterator = typename ints::iterator;
using const_iterator = typename ints::const_iterator;
iterator begin() { PING(); return std::begin(c_); }
iterator end() { PING(); return std::end(c_); }
const_iterator begin() const { PING(); return std::cbegin(c_); }
const_iterator end() const { PING(); return std::cend(c_); }
ints c_ = { 1, 2, 3, 4, 5 };
};
int main()
{
auto r = container{} | boost::adaptors::filtered([](auto&& v) { return v % 2; });
std::cerr << "Loop\n";
for (auto i: r)
std::cout << i << '\n';
}
It results in (live code):
container::container()
const_iterator container::begin() const
const_iterator container::end() const
const_iterator container::end() const
const_iterator container::end() const
container::~container()
Loop
1
3
5
Is there a simple way to ensure that everybody lives as long as I need it? Of course in main I could declare a variable to store the container{}, but that's inadequate for my real world use where this container is actually obtained by querying some objects ; I don't want the client side to have to deal with this.
It seems that the simplest would be to rewrite some version of filtered that would keep a copy of the range, but I'd like to look for a solution that would avoid writing too much code. And I'm really looking for a Range-v2 solution: it's probably too soon for me to depend upon Range-v3.
So this is nasty.
The trick is you have to store the container first, then apply the pipe to the stored container, then pretend to be that range-like.
template<class X>struct store{ X data; };
template<class Src, class Range>
struct save_src_range:
private store<Src>,
Range
{
// boilerplate for copy/move goes here (TODO)
template<class S, class RangeFactory>
save_src_range( S&& s, RangeFactory&& f ):
store<Src>{std::forward<S>(s)},
Range( std::forward<RangeFactory>(f)(this->data) )
{}
};
now that needs to be gussed up with a deducing creation function and the like.
Next, we need a syntactically pretty way to insert that capability into the existing syntax.
One approach is like:
keep_a_copy( source ) | boost::adapters::filter( ... blah ... )
where we do some judo and make it work magically, maybe even after chaining.
Or
source | keep_source_copy( boost::adapters::filter( ... blah ... ) )
which is a bit easier I think.
I took a stab at it, and it is modestly painful, but I don't see anything fundamentally impossible. It definitely involves writing too much code.
Is there a convenient way to get the index of the current container entry in a C++11 foreach loop, like enumerate in python:
for idx, obj in enumerate(container):
pass
I could imagine an iterator that can also return the index or similar.
Of course I could have a counter, but often iterators don't give guarantees of the order they iterate over a container.
A good implementation of the feature you are requested can be found here:
https://github.com/ignatz/pythonic
The idea behind is, that you build a wrapper struct with a custom iterator that does the counting. Below is a very minimal exemplary implementation to illustrate the idea:
// Distributed under the terms of the GPLv2 or newer
#include <iostream>
#include <vector>
#include <tuple>
// Wrapper class
template <typename T>
class enumerate_impl
{
public:
// The return value of the operator* of the iterator, this
// is what you will get inside of the for loop
struct item
{
size_t index;
typename T::value_type & item;
};
typedef item value_type;
// Custom iterator with minimal interface
struct iterator
{
iterator(typename T::iterator _it, size_t counter=0) :
it(_it), counter(counter)
{}
iterator operator++()
{
return iterator(++it, ++counter);
}
bool operator!=(iterator other)
{
return it != other.it;
}
typename T::iterator::value_type item()
{
return *it;
}
value_type operator*()
{
return value_type{counter, *it};
}
size_t index()
{
return counter;
}
private:
typename T::iterator it;
size_t counter;
};
enumerate_impl(T & t) : container(t) {}
iterator begin()
{
return iterator(container.begin());
}
iterator end()
{
return iterator(container.end());
}
private:
T & container;
};
// A templated free function allows you to create the wrapper class
// conveniently
template <typename T>
enumerate_impl<T> enumerate(T & t)
{
return enumerate_impl<T>(t);
}
int main()
{
std::vector<int> data = {523, 1, 3};
for (auto x : enumerate(data))
{
std::cout << x.index << ": " << x.item << std::endl;
}
}
What about a simple solution like:
int counter=0;
for (auto &val: container)
{
makeStuff(val, counter);
counter++;
}
You could make a bit more "difficult" to add code after the counter by adding a scope:
int counter=0;
for (auto &val: container)
{{
makeStuff(val, counter);
}counter++;}
As #graham.reeds pointed, normal for loop is also a solution, that could be as fast:
int counter=0;
for (auto it=container.begin(); it!=container.end(); ++it, ++counter)
{
makeStuff(val, counter);
}
And finally, a alternative way using algorithm:
int counter = 0;
std::for_each(container.begin(), container.end(), [&counter](int &val){
makeStuff(val, counter++);
});
Note: the order between range loop and normal loop is guaranteed by the standard 6.5.4. Meaning the counter is able to be coherent with the position in the container.
If you have access to Boost its range adaptors can be used like this:
#include <boost/range/adaptor/indexed.hpp>
using namespace boost::adaptors;
for (auto const& elem : container | indexed(0))
{
std::cout << elem.index() << " - " << elem.value() << '\n';
}
Source (where there are also other examples)
C++17 and structured bindings makes this look OK - certainly better than some ugly mutable lambda with a local [i = 0](Element&) mutable or whatever I've done before admitting that probably not everything should be shoehorned into for_each() et al. - and than other solutions that require a counter with scope outside the for loop.
for (auto [it, end, i] = std::tuple{container.cbegin(), container.cend(), 0};
it != end; ++it, ++i)
{
// something that needs both `it` and `i`ndex
}
You could make this generic, if you use this pattern often enough:
template <typename Container>
auto
its_and_idx(Container&& container)
{
using std::begin, std::end;
return std::tuple{begin(container), end(container), 0};
}
// ...
for (auto [it, end, i] = its_and_idx(foo); it != end; ++it, ++i)
{
// something
}
C++ Standard proposal P2164 proposes to add views::enumerate, which would provide a view of a range giving both reference-to-element and index-of-element to a user iterating it.
We propose a view enumerate whose value type is a struct with 2 members index and value representing respectively the position and value of the elements in the adapted range.
[ . . .]
This feature exists in some form in Python, Rust, Go (backed into the language), and in many C++ libraries: ranges-v3, folly, boost::ranges (indexed).
The existence of this feature or lack thereof is the subject of recurring stackoverflow questions.
Hey, look! We're famous.
If you need the index then a traditional for works perfectly well.
for (int idx=0; idx<num; ++idx)
{
// do stuff
}
There is a range-based for loop with the syntax:
for(auto& i : array)
It works with constant arrays but not with pointer based dynamic ones, like
int *array = new int[size];
for(auto& i : array)
cout<< i << endl;
It gives errors and warnings about failure of substitution, for instance:
Error] C:\Users\Siegfred\Documents\C-Free\Temp\Untitled2.cpp:16:16: error: no matching function for call to 'begin(int*&)'
How do I use this new syntax with dynamic arrays?
To make use of the range-based for-loop you have to provide either begin() and end() member functions or overload the non-member begin() and end() functions.
In the latter case, you can wrap your range in a std::pair and overload begin() and end() for those:
namespace std {
template <typename T> T* begin(std::pair<T*, T*> const& p)
{ return p.first; }
template <typename T> T* end(std::pair<T*, T*> const& p)
{ return p.second; }
}
Now you can use the for-loop like this:
for (auto&& i : std::make_pair(array, array + size))
cout << i << endl;
Note, that the non-member begin() and end() functions have to be overloaded in the std namespace here, because pair also resides in namespace std. If you don't feel like tampering with the standard namespace, you can simply create your own tiny pair class and overload begin() and end() in your namespace.
Or, create a thin wrapper around your dynamically allocated array and provide begin() and end() member functions:
template <typename T>
struct wrapped_array {
wrapped_array(T* first, T* last) : begin_ {first}, end_ {last} {}
wrapped_array(T* first, std::ptrdiff_t size)
: wrapped_array {first, first + size} {}
T* begin() const noexcept { return begin_; }
T* end() const noexcept { return end_; }
T* begin_;
T* end_;
};
template <typename T>
wrapped_array<T> wrap_array(T* first, std::ptrdiff_t size) noexcept
{ return {first, size}; }
And your call site looks like this:
for (auto&& i : wrap_array(array, size))
std::cout << i << std::endl;
Example
You can't use range-for-loop with dynamically allocated arrays, since compiler can't deduce begin and end of this array. You should always use containers instead of it, for example std::vector.
std::vector<int> v(size);
for(const auto& elem: v)
// do something
You can't perform a range based loop directly over a dynamically allocated array because all you have is a pointer to the first element. There is no information concerning its size that the compiler can use to perform the loop. The idiomatic C++ solution would be to replace the dynamically allocated array by an std::vector:
std::vector<int> arr(size);
for(const auto& i : arr)
std::cout<< i << std::endl;
Alternatively, you could use a range type that provides a begin and end iterator based on a pointer and an offset. Have a look at some of the types in the boost.range library, or at the GSL span proposal (example implementation here, reference for C++20 proposed type here).
Note that a range based for loop does work for std::array objects of fixes size plain arrays:
std::array<int,10> arr;
for(const auto& i : arr)
std::cout<< i << std::endl;
int arr[10] = .... ;
for(const auto& i : arr)
std::cout<< i << std::endl;
but in both cases the size needs to be a compile-time constant.
C++20 adds std::span, which allows looping like this:
#include <iostream>
#include <span>
int main () {
auto p = new int[5];
for (auto &v : std::span(p, 5)) {
v = 1;
}
for (auto v : std::span(p, 5)) {
std::cout << v << '\n';
}
delete[] p;
}
This is supported by current compilers, e.g. gcc 10.1 and clang 7.0.0 and later. (Live)
Of course, if you have the choice, it is preferable to use std::vector over C-style arrays from the get-go.
Instead of defining std::begin and std::end for std::pair of pointers (defining them in std::, by the way, is undefined behaviour) and rolling out your own wrapper, as suggested before, you can use boost::make_iterator_range:
size_t size = 16;
int *dynamic_array = new int[size];
for (const auto& i : boost::make_iterator_range(dynamic_array, dynamic_array + size))
std::cout << i << std::endl;
Live example.
From C++20 views, we can use subrange also (std::ranges::subrange)
auto p = new int[5];
//INPUT DATA TO TEST
int i = 0;
for (auto& v : std::ranges::subrange(p, p + 5)) {
v = ++i;
}
//USE SUBRANGE
for (auto v : std::ranges::subrange(p, p + 5))
std::cout << v << '\n';
I'm new to C/C++ programming, but I've been programming in C# for 1.5 years now. I like C# and I like the List class, so I thought about making a List class in C++ as an exercise.
List<int> ls;
int whatever = 123;
ls.Add(1);
ls.Add(235445);
ls.Add(whatever);
The implementation is similar to any Array List class out there. I have a T* vector member where I store the items, and when this storage is about to be completely filled, I resize it.
Please notice that this is not to be used in production, this is only an exercise. I'm well aware of vector<T> and friends.
Now I want to loop through the items of my list. I don't like to use for(int i=0;i<n; i==). I typed for in the visual studio, awaited for Intellisense, and it suggested me this:
for each (object var in collection_to_loop)
{
}
This obviously won't work with my List implementation. I figured I could do some macro magic, but this feels like a huge hack. Actually, what bothers me the most is passing the type like that:
#define foreach(type, var, list)\
int _i_ = 0;\
##type var;\
for (_i_ = 0, var=list[_i_]; _i_<list.Length();_i_++,var=list[_i_])
foreach(int,i,ls){
doWork(i);
}
My question is: is there a way to make this custom List class work with a foreach-like loop?
Firstly, the syntax of a for-each loop in C++ is different from C# (it's also called a range based for loop. It has the form:
for(<type> <name> : <collection>) { ... }
So for example, with an std::vector<int> vec, it would be something like:
for(int i : vec) { ... }
Under the covers, this effectively uses the begin() and end() member functions, which return iterators. Hence, to allow your custom class to utilize a for-each loop, you need to provide a begin() and an end() function. These are generally overloaded, returning either an iterator or a const_iterator. Implementing iterators can be tricky, although with a vector-like class it's not too hard.
template <typename T>
struct List
{
T* store;
std::size_t size;
typedef T* iterator;
typedef const T* const_iterator;
....
iterator begin() { return &store[0]; }
const_iterator begin() const { return &store[0]; }
iterator end() { return &store[size]; }
const_iterator end() const { return &store[size]; }
...
};
With these implemented, you can utilize a range based loop as above.
Let iterable be of type Iterable.
Then, in order to make
for (Type x : iterable)
compile, there must be types called Type and IType and there must be functions
IType Iterable::begin()
IType Iterable::end()
IType must provide the functions
Type operator*()
void operator++()
bool operator!=(IType)
The whole construction is really sophisticated syntactic sugar for something like
for (IType it = iterable.begin(); it != iterable.end(); ++it) {
Type x = *it;
...
}
where instead of Type, any compatible type (such as const Type or Type&) can be used, which will have the expected implications (constness, reference-instead-of-copy etc.).
Since the whole expansion happens syntactically, you can also change the declaration of the operators a bit, e.g. having *it return a reference or having != take a const IType& rhs as needed.
Note that you cannot use the for (Type& x : iterable) form if *it does not return a reference (but if it returns a reference, you can also use the copy version).
Note also that operator++() defines the prefix version of the ++ operator -- however it will also be used as the postfix operator unless you explicitly define a postfix ++. The ranged-for will not compile if you only supply a postfix ++, which btw.can be declared as operator++(int) (dummy int argument).
Minimal working example:
#include <stdio.h>
typedef int Type;
struct IType {
Type* p;
IType(Type* p) : p(p) {}
bool operator!=(IType rhs) {return p != rhs.p;}
Type& operator*() {return *p;}
void operator++() {++p;}
};
const int SIZE = 10;
struct Iterable {
Type data[SIZE];
IType begin() {return IType(data); }
IType end() {return IType(data + SIZE);}
};
Iterable iterable;
int main() {
int i = 0;
for (Type& x : iterable) {
x = i++;
}
for (Type x : iterable) {
printf("%d", x);
}
}
output
0123456789
You can fake the ranged-for-each (e.g. for older C++ compilers) with the following macro:
#define ln(l, x) x##l // creates unique labels
#define l(x,y) ln(x,y)
#define for_each(T,x,iterable) for (bool _run = true;_run;_run = false) for (auto it = iterable.begin(); it != iterable.end(); ++it)\
if (1) {\
_run = true; goto l(__LINE__,body); l(__LINE__,cont): _run = true; continue; l(__LINE__,finish): break;\
} else\
while (1) \
if (1) {\
if (!_run) goto l(__LINE__,cont);/* we reach here if the block terminated normally/via continue */ \
goto l(__LINE__,finish);/* we reach here if the block terminated by break */\
} \
else\
l(__LINE__,body): for (T x = *it;_run;_run=false) /* block following the expanded macro */
int main() {
int i = 0;
for_each(Type&, x, iterable) {
i++;
if (i > 5) break;
x = i;
}
for_each(Type, x, iterable) {
printf("%d", x);
}
while (1);
}
(use declspec or pass IType if your compiler doesn't even have auto).
Output:
1234500000
As you can see, continue and break will work with this thanks to its complicated construction.
See http://www.chiark.greenend.org.uk/~sgtatham/mp/ for more C-preprocessor hacking to create custom control structures.
That syntax Intellisense suggested is not C++; or it's some MSVC extension.
C++11 has range-based for loops for iterating over the elements of a container. You need to implement begin() and end() member functions for your class that will return iterators to the first element, and one past the last element respectively. That, of course, means you need to implement suitable iterators for your class as well. If you really want to go this route, you may want to look at Boost.IteratorFacade; it reduces a lot of the pain of implementing iterators yourself.
After that you'll be able to write this:
for( auto const& l : ls ) {
// do something with l
}
Also, since you're new to C++, I want to make sure that you know the standard library has several container classes.
C++ does not have the for_each loop feature in its syntax. You have to use c++11 or use the template function std::for_each.
#include <vector>
#include <algorithm>
#include <iostream>
struct Sum {
Sum() { sum = 0; }
void operator()(int n) { sum += n; }
int sum;
};
int main()
{
std::vector<int> nums{3, 4, 2, 9, 15, 267};
std::cout << "before: ";
for (auto n : nums) {
std::cout << n << " ";
}
std::cout << '\n';
std::for_each(nums.begin(), nums.end(), [](int &n){ n++; });
Sum s = std::for_each(nums.begin(), nums.end(), Sum());
std::cout << "after: ";
for (auto n : nums) {
std::cout << n << " ";
}
std::cout << '\n';
std::cout << "sum: " << s.sum << '\n';
}
As #yngum suggests, you can get the VC++ for each extension to work with any arbitrary collection type by defining begin() and end() methods on the collection to return a custom iterator. Your iterator in turn has to implement the necessary interface (dereference operator, increment operator, etc). I've done this to wrap all of the MFC collection classes for legacy code. It's a bit of work, but can be done.