Repeat n times with C++ ranges without dummy variable - c++

I am trying to implement repeat n times using C++20 ranges.
It works, but all approaches I can think of need a dummy variable (one for the for loop variable, other for arity of lambda).
static constexpr int kIterations = 3;
void f1(){
auto c = '.';
for (const auto _ :std::views::iota(0)| std::views::take(kIterations) ) {
std::cout << c;
}
std::cout << std::endl;
}
void f2(){
auto c = '.';
std::ranges::for_each(std::views::iota(0)| std::views::take(kIterations), [&c](const auto _) {
std::cout << c;
});
std::cout << std::endl;
}
Is there a way to do this with C++20 ranges without the need for dummy variable (_ in my case, but even unnamed requires const? auto).
notes:
I know I can use C for loop🙂, I am interested in doing this in C++20 ranges way.
I guess I can implement this in an algorithm myself, but I wonder if I can do it "inline" without me implementing repeat_n
I know iota has version that takes 2 integers, it might be more readable, but still does not fix the problem with dummy variables.
logic is simplified, just to show capture

A range is a means of iterating over a set of values. You can manufacture those values on demand rather than accessing them from storage, as iota does. But ranges are about iterating over a sequence of values.
So any range-based iteration mechanism is going to involve an object representing, at least, the current position in the range. That's just the nature of the beast.
Yes, you could write your own version of ranges::for_each which "iterates" over a range but doesn't actually pass those values to the functor:
template<std::ranges::input_range Rng, std::invocable Func>
decltype(auto) iterate_no_element(Rng &&rng, Func func)
{
return std::ranges::for_each(std::forward<Rng>(rng), [func](auto const&){func();})
}
But the standard has no such function.

If you could use both C++20 <ranges> and repeat_n from Range-v3, then this could be an approach?
#include <algorithm>
#include <functional>
#include <iostream>
#include <boost/hof/lift.hpp>
#include <range/v3/view/repeat_n.hpp>
#include <ranges>
using ranges::views::repeat_n;
using namespace std::ranges::views;
using namespace std::ranges;
auto constexpr invoke = BOOST_HOF_LIFT(std::invoke); // what a pity we need this
static constexpr int kIterations = 3;
void f() {
char c = '.';
for_each(repeat_n([c]{ std::cout << c << std::endl; },
kIterations),
invoke);
}
int main()
{
f();
}
The lambda [c]{ std::cout << c << std::endl; } is nullary as it isn't passed any value, and there's no for loop with a dummy variable.

Related

Accommodation for dynamic array

From this discussion, I have the following code to check if an element exists in an array:
#include <iostream>
#include <vector>
template <typename T, std::size_t N>
bool IsIn(T value, const T(&values)[N])
{
for (const T& array_value : values)
{
if (value == array_value) return true;
}
return false;
}
int main() {
int arr1[] = { 10, 20, 30 };
bool ee1 = IsIn(10, arr1);
std::cout << "ee1 = " << (ee1?"true":"false") << "\n";
return 0;
}
I believe this code is good for array of fixed size (at compile time) only. If the array is dynamically created (the number of elements is not known at compile time), is there any way I can modify the code to accommodate it?
PS: I am aware of vector. However, I am just curious if there is any way to avoid it.
Don't use C-style arrays unless you absolutely need to. Use std::array instead. For dynamic arrays, use std::vector.
You can then use iterators to make your function generic. However, this function already exists, it's called std::find. You can try to implement your own, for learning purposes, or look up an example implementation here: cppreference | find
#include <algorithm>
#include <array>
#include <iostream>
#include <string>
#include <vector>
int main(){
std::array<int, 3> static_array{1, 2, 3};
std::vector<int> dynamic_array{3, 4, 5};
std::string str = "Hello World";
std::array<int, 3>::iterator stat_found;
if( (stat_found = std::find(static_array.begin(), static_array.end(), 3)) != static_array.end() ){
std::cout << "Found 3 in static_array at pos: " << stat_found - static_array.begin() << "\n";
}
std::vector<int>::iterator dyn_found;
if( (dyn_found = std::find(dynamic_array.begin(), dynamic_array.end(), 3)) != dynamic_array.end() ){
std::cout << "Found 3 in dynamic_array at pos: " << dyn_found - dynamic_array.begin() << "\n";
}
std::string::iterator str_found;
if( (str_found = std::find(str.begin(), str.end(), 'W')) != str.end() ){
std::cout << "Found W in string at pos: " << str_found - str.begin() << "\n";
}
}
Without changing the body of your method, you can accommodate practically any collection type by abstracting over the collection type as well, i.e.
template <typename T, typename Collection>
bool IsIn(T value, const Collection &values)
{
/* ... */
}
However, as inifnitezero noted, the standard way of doing this is actually with iterators, and many implementations already exist in the standard library for this.
For a dynamic array you have to pass in the size in some form or another.
template <typename T>
bool IsIn(T value, const T *arr, std::size_t size) { ... }
You already know about std::vector, which knows it's own size, so I will skip that. That is the way to handle dynamic arrays. But not the only way to pass them to a function.
You can use std::span, which can be used for fixed sized arrays, std::array, std::vector and any container with random access iterator (sequential iterator? not sure). It's probably the most flexible thing to use.
You can also use begin and end const iterators. But that involves a lot of typing unless you already have 2 iterators when you want to call it.
Personally I think std::span covers all the bases. You can even make a span from iterators.

Why passing struct function to for_each loop does not change the struct attributes

I'm new to C++ and I'm learning with C++20. I'm trying on a struct function, which is to wrap a function in a struct, while we can claim local attributes in this struct.
The thing is that when I pass this struct function to a for_each function, it does not work.
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
struct accumulateAmount{
int total_amount;
accumulateAmount() { total_amount = 100 ;} //constructor
void operator()(int num){
total_amount += num;
}
};
int main(){
vector<int> nums{1,2,3,4,5};
accumulateAmount acctor;
for_each(nums.begin(), nums.end(), acctor);
cout << acctor.total_amount << endl;
return 0;
}
The output is 100. It does not realize the accumulator functionality.
While if I change the loop from for_each to ordinary for loop as the following:
for (int i = 0; i < nums.size(); i++){
acctor(nums[i]);
}
It works.
So I wonder if it's because 'for_each' encompasses parallel computing hence for each int in the vector, we are using independent functions on them?
std::for_each takes the function by value. So your function gets copied, and std::for_each calls the copy. That’s why your acctor does not get modified.
You can force passing by reference though, by using std::ref:
for_each(nums.begin(), nums.end(), std::ref(acctor));
Alternatively, and perhaps more idiomatically, you can capture the return value of std::for_each:
auto const result = for_each(nums.begin(), nums.end(), accumulateAmount());
std::cout << result.total_amount << "\n";
The good thing about this code is that you don’t even need to introduce a name for acctor: you can pass a temporary and create the function object on the fly. This is nice because it means that you can make all your local objects const.
That said, std::for_each with a mutable function object is absolutely not idiomatic C++. Finding the suitable algorithm isn’t always obvious, but always worth it. In this case, you’d use std::reduce:
auto const result = std::reduce(nums.begin(), nums.end(), 100);
std::cout << result << "\n";
The quickest fix:
acctor = std::for_each(nums.begin(), nums.end(), accumulateAmount());
But rather than spinning your own functor from scratch, use C++'s lambdas (C++11+).
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
int main(){
std::vector<int> nums{1,2,3,4,5};
int val = 100;
std::for_each(nums.begin(), nums.end(), [&val](int n) { return val += n; });
std::cout << val << '\n';
return 0;
}
Some quick notes on the lambda:
[] is where you can 'capture' variables outside the scope of the lambda, that shouldn't be passed as arguments. In this case I capture val by reference. You are not required to capture anything, but the [] is required.
() parameter list, pretty straightforward.
{} function body, also straightforward.
As pointed out in a comment to another answer, this specific example is solved even simpler with std::accumulate or std::reduce (Shown in another answer (this one was new to me, and pretty cool)).
#include <iostream>
#include <numeric>
#include <string>
#include <vector>
int main() {
std::vector<int> nums{1, 2, 3, 4, 5};
std::cout << std::accumulate(nums.begin(), nums.end(), 100) << '\n';
return 0;
}

Copy vector of vectors into 1D array

I have following C++ object
std::vector<std::vector<SomeClass>> someClassVectors(sizeOFOuter);
where I know the size of "outer" vector, but sizes of "inner" vectors varies. I need to copy the elements of this structure into 1D array like this:
SomeClass * someClassArray;
I have a solution where I use std::copy like this
int count = 0;
for (int i = 0; i < sizeOfOuter; i++)
{
std::copy(someClassVectors[i].begin(), someClassVectors[i].end(), &someClassArray[count]);
count += someClassVectors[i].size();
}
but the class includes large matrices which means I cannot have the "vectors" structure and 1D array allocated twice at the same time.
Any ideas?
Do you previously preallocate someClassArray to a given size? I'd suggest using 1D vector for getting rid of known problems with the plain array if possible.
what about something like this:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main() {
std::vector<std::vector<int>> someClassVectors {
{1,2,3},
{4,5,6},
{7,8,9}
};
std::vector<int> flat;
while (!someClassVectors.empty())
{
auto& last = someClassVectors.back();
std::move(std::rbegin(last), std::rend(last), std::back_inserter(flat));
someClassVectors.pop_back();
}
std::reverse(std::begin(flat), std::end(flat));
int * someClassArray = flat.data();
std::copy(someClassArray, someClassArray + flat.size(), std::ostream_iterator<int>(std::cout, " "));
}
The extra reverse operation doesn't have an effect on memory metrics - such an approach helps to avoid unneeded memory reallocations resulting from removing vector elements from beginning to end.
EDIT
Inspired by comments I changed copy to move semantics
Embrace Range-v3 (or whatever will be introduced in C++20) and write a solution in (almost) a single line:
auto flattenedRange = ranges::views::join(someClassVectors);
this gives you a range in flattenedRange, which you can loop over or copy somewhere else easily.
This is a possible use case:
#include <iostream>
#include <vector>
#include <range/v3/view/join.hpp>
int main()
{
std::vector<std::vector<int>> Ints2D = {
{1,2,3},
{4},
{5,6}
};
auto Ints1D = ranges::views::join(Ints2D);
// here, going from Ints1D to a C-style array is easy, and shown in the other answer already
for (auto const& Int : Ints1D) {
std::cout << Int << ' ';
}
std::cout << '\n';
// output is: 1 2 3 4 5 6
}
In case you want to get a true std::vector instead of a range, before writing it into a C-style array, you can include this other header
#include <range/v3/range/conversion.hpp>
and pipe join's output into a conversion function:
auto Ints1D = ranges::views::join(Ints2D) | ranges::to_vector;
// auto deduces std::vector<int>
In terms of standard and versions, it doesn't really require much. In this demo you can see that it compiles and runs just fine with
compiler GCC 7.3
library Range-v3 0.9.1
C++14 standard (option -std=c++14 to g++)
As regards the copies
ranges::views::join(Ints2D) is only creating a view on Ints2D, so no copy happens; if view doesn't make sense to you, you might want to give a look at Chapter 7 from Functional Programming in C++, which has a very clear explanation of ranges, with pictures and everything;¹
even assigning that output to a variable, auto Ints1D = ranges::views::join(Ints2D);, does not trigger a copy; Ints1D in this case is not a std::vector<int>, even though it behaves as one when we loop on it (behaves as a vector because it's a view on it);
converting it to a vector, e.g. via | ranges::to_vector, obviously triggers a copy, because you are no more requesting a view on a vector, but a true one;
passing the range to an algorithm which loops on its elements doesn't trigger a copy.
Here's an example code that you can try out:
// STL
#include <iostream>
#include <vector>
// Boost and Range-v3
#include <boost/range/algorithm/for_each.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/range/conversion.hpp>
struct A {
A() = default;
A(A const&) { std::cout << "copy ctor\n"; };
};
int main()
{
std::vector<std::vector<A>> Ints2D = {
{A{},A{}},
{A{},A{}}
};
using boost::range::for_each;
using ranges::to_vector;
using ranges::views::join;
std::cout << "no copy, because you're happy with the range\n";
auto Ints1Dview = join(Ints2D);
std::cout << "copy, because you want a true vector\n";
auto Ints1D = join(Ints2D) | to_vector;
std::cout << "copy, despite the refernce, because you need a true vector\n";
auto const& Ints1Dref = join(Ints2D) | to_vector;
std::cout << "no copy, because we movedd\n";
auto const& Ints1Dref_ = join(std::move(Ints2D)) | to_vector;
std::cout << "no copy\n";
for_each(join(Ints2D), [](auto const&){ std::cout << "hello\n"; });
}
¹ In an attempt to try giving a clue of what a range is, I would say that you can imagine it as a thing wrapping two iterators, one poiting to the end of the range, the other one pointing to the begin of the range, the latter being incrementable via operator++; this opearator will take care of the jumps in the correct way, for instance, after viewing the element 3 in Ints2D (which is in Ints2D[0][2]), operator++ will make the iterator jump to view the elment Ints[1][0].

Why structured bindings only work with auto

Structured bindings have been introduced with c++17. They give the ability to declare multiple variables initialised from a tuple or struct.
This code compiles using a c++17 compiler.
#include <iostream>
#include <tuple>
int main() {
auto tuple = std::make_tuple(1.0, 1);
auto [ d, i ] = tuple;
std::cout << "d=" << d << " i=" << i << '\n';
return 0;
}
If I don't declare the variables with auto I get the error
error: expected body of lambda expression
[d2 , i2] = tuple;
#include <iostream>
#include <tuple>
int main() {
auto tuple = std::make_tuple(1.0, 2);
double d2;
int i2;
[d2 , i2] = tuple;
return 0;
}
I used clang version 4.0.0 and the compile option -std=c++1z.
Can I assign existing variables to a structured binding? Do I need to use auto?
The error message your got is pretty indicative of why it's only allowed with auto: lack of ambiguity that will make the grammar even more context dependent.
A pair of square brackets at the start of an expression indicates a lambda. What you are asking is for the standard to specify that sometimes [d2 , i2] is the beginning of a lambda that captures d2 and i2 by value, and at other times it's an unpacking assignment. All based on what follows it.
It's just not worth the complexity to add it to the language. Especially, since as Some programmer dude noted, you already have std::tie to do what you want with tuples.
Not only that, std::tie allows you to ignore some of the unpacked values, something structured bindings don't support yet. So it all boils down to having a more limited form of syntactic sugar, to do something the standard library already does with tuples.
Oh, and if you are disgruntles that std::tie works only with tuples, you can expand it to work with any POD yourself. Just look at this magic_get implementation. One can apply the same idea to constexpr transform a POD into a tuple of reference that can be fed to std::tie. Something like this:
std::tie(d2, i2) = magic_unpack(/*some POD that isn't a tuple*/);
Also, you can using std::tie() to unpack the tuple into its individual components. Such as
#include <iostream>
#include <tuple>
int main() {
auto tuple = std::make_tuple(1.0, 1);
double d2;
int i2;
std::tie(d2, i2) = tuple;
std::cout << "d2=" << d2 << " i2=" << i2 << '\n';
return 0;
}

C++ range/xrange equivalent in STL or boost?

Is there C++ equivalent for python Xrange generator in either STL or boost?
xrange basically generates incremented number with each call to ++ operator.
the constructor is like this:
xrange(first, last, increment)
was hoping to do something like this using boost for each:
foreach(int i, xrange(N))
I. am aware of the for loop. in my opinion they are too much boilerplate.
Thanks
my reasons:
my main reason for wanting to do so is because i use speech to text software, and programming loop usual way is difficult, even if using code completion. It is much more efficient to have pronounceable constructs.
many loops start with zero and increment by one, which is default for range. I find python construct more intuitive
for(int i = 0; i < N; ++i)
foreach(int i, range(N))
functions which need to take range as argument:
Function(int start, int and, int inc);
function(xrange r);
I understand differences between languages, however if a particular construct in python is very useful for me and can be implemented efficiently in C++, I do not see a reason not to use it. For each construct is foreign to C++ as well however people use it.
I put my implementation at the bottom of the page as well the example usage.
in my domain i work with multidimensional arrays, often rank 4 tensor. so I would often end up with 4 nested loops with different ranges/increments to compute normalization, indexes, etc. those are not necessarily performance loops, and I am more concerned with correctness readability and ability to modify.
for example
int function(int ifirst, int ilast, int jfirst, int jlast, ...);
versus
int function(range irange, range jrange, ...);
In the above, if different strids are needed, you have to pass more variables, modify loops, etc. eventually you end up with a mass of integers/nearly identical loops.
foreach and range solve my problem exactly. familiarity to average C++ programmer is not high on my list of concerns - problem domain is a rather obscure, there is a lot of meta-programming, SSE intrinsic, generated code.
Boost irange should really be the answer (ThxPaul Brannan)
I'm adding my answer to provide a compelling example of very valid use-cases that are not served well by manual looping:
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/irange.hpp>
using namespace boost::adaptors;
static int mod7(int v)
{ return v % 7; }
int main()
{
std::vector<int> v;
boost::copy(
boost::irange(1,100) | transformed(mod7),
std::back_inserter(v));
boost::sort(v);
boost::copy(
v | reversed | uniqued,
std::ostream_iterator<int>(std::cout, ", "));
}
Output: 6, 5, 4, 3, 2, 1, 0,
Note how this resembles generators/comprehensions (functional languages) and enumerables (C#)
Update I just thought I'd mention the following (highly inflexible) idiom that C++11 allows:
for (int x : {1,2,3,4,5,6,7})
std::cout << x << std::endl;
of course you could marry it with irange:
for (int x : boost::irange(1,8))
std::cout << x << std::endl;
Boost has counting_iterator as far as I know, which seems to allow only incrementing in steps of 1. For full xrange functionality you might need to implement a similar iterator yourself.
All in all it could look like this (edit: added an iterator for the third overload of xrange, to play around with boost's iterator facade):
#include <iostream>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/foreach.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <cassert>
template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T to)
{
//these assertions are somewhat problematic:
//might produce warnings, if T is unsigned
assert(T() <= to);
return boost::make_iterator_range(boost::counting_iterator<T>(0), boost::counting_iterator<T>(to));
}
template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T from, T to)
{
assert(from <= to);
return boost::make_iterator_range(boost::counting_iterator<T>(from), boost::counting_iterator<T>(to));
}
//iterator that can do increments in steps (positive and negative)
template <class T>
class xrange_iterator:
public boost::iterator_facade<xrange_iterator<T>, const T, std::forward_iterator_tag>
{
T value, incr;
public:
xrange_iterator(T value, T incr = T()): value(value), incr(incr) {}
private:
friend class boost::iterator_core_access;
void increment() { value += incr; }
bool equal(const xrange_iterator& other) const
{
//this is probably somewhat problematic, assuming that the "end iterator"
//is always the right-hand value?
return (incr >= 0 && value >= other.value) || (incr < 0 && value <= other.value);
}
const T& dereference() const { return value; }
};
template <class T>
boost::iterator_range<xrange_iterator<T> > xrange(T from, T to, T increment)
{
assert((increment >= T() && from <= to) || (increment < T() && from >= to));
return boost::make_iterator_range(xrange_iterator<T>(from, increment), xrange_iterator<T>(to));
}
int main()
{
BOOST_FOREACH(int i, xrange(10)) {
std::cout << i << ' ';
}
BOOST_FOREACH(int i, xrange(10, 20)) {
std::cout << i << ' ';
}
std::cout << '\n';
BOOST_FOREACH(int i, xrange(0, 46, 5)) {
std::cout << i << ' ';
}
BOOST_FOREACH(int i, xrange(10, 0, -1)) {
std::cout << i << ' ';
}
}
As others are saying, I don't see this buying you much over a normal for loop.
std::iota (not yet standardized) is kinda like range. Doesn't make things any shorter or clearer than an explicit for loop, though.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
int main() {
std::vector<int> nums(5);
std::iota(nums.begin(), nums.end(), 1);
std::copy(nums.begin(), nums.end(),
std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
return 0;
}
Compile with g++ -std=c++0x; this prints "1 2 3 4 5 \n".
well, here is what i wrote, since there does not seem to be one.
the generator does not use any internal storage besides single integer.
range object can be passed around and used in nested loops.
there is a small test case.
#include "iostream"
#include "foreach.hpp"
#include "boost/iterator/iterator_categories.hpp"
struct range {
struct iterator_type {
typedef int value_type;
typedef int difference_type;
typedef boost::single_pass_traversal_tag iterator_category;
typedef const value_type* pointer;
typedef const value_type & reference;
mutable value_type value;
const difference_type increment;
iterator_type(value_type value, difference_type increment = 0)
: value(value), increment(increment) {}
bool operator==(const iterator_type &rhs) const {
return value >= rhs.value;
}
value_type operator++() const { return value += increment; }
operator pointer() const { return &value; }
};
typedef iterator_type iterator;
typedef const iterator_type const_iterator;
int first_, last_, increment_;
range(int last) : first_(0), last_(last), increment_(1) {}
range(int first, int last, int increment = 1)
: first_(first), last_(last), increment_(increment) {}
iterator begin() const {return iterator(first_, increment_);}
iterator end() const {return iterator(last_);}
};
int test(const range & range0, const range & range1){
foreach(int i, range0) {
foreach(int j, range1) {
std::cout << i << " " << j << "\n";
}
}
}
int main() {
test(range(6), range(3, 10, 3));
}
my main reason for wanting to do so is because i use speech to text software, and programming loop usual way is difficult, even if using code completion. It is much more efficient to have pronounceable constructs.
That makes sense. But couldn't a simple macro solve this problem? #define for_i_to(N, body) for (int i = 0; i < N; ++i) { body }
or something similar. Or avoid the loop entirely and use the standard library algorithms. (std::for_each(range.begin(), rang.end(), myfunctor()) seems easier to pronounce)
many loops start with zero and increment by one, which is default for range. I find python construct more intuitive
You're wrong. The Python version is more intuitive to a Python programmer. And it may be more intuitive to a non-programmer. But you're writing C++ code. Your goal should be to make it intuitive to a C++ programmer. And C++ programmer know for-loops and they know the standard library algorithms. Stick to using those. (Or stick to writing Python)
functions which need to take range as argument:
Function(int start, int and, int inc);
function(xrange r);
Or the idiomatic C++ version:
template <typename iter_type>
void function(iter_type first, iter_type last);
In C++, ranges are represented by iterator pairs. Not integers.
If you're going to write code in a new language, respect the conventions of that language. Even if it means you have to adapt and change some habits.
If you're not willing to do that, stick with the language you know.
Trying to turn language X into language Y is always the wrong thing to do. It own't work, and it'll confuse the language X programmers who are going to maintain (or just read) your code.
Since I've started to use BOOST_FOREACH for all my iteration (probably a misguided idea, but that's another story), here's another use for aaa's range class:
std::vector<int> vec;
// ... fill the vector ...
BOOST_FOREACH(size_t idx, make_range(0, vec.size()))
{
// ... do some stuff ...
}
(yes, range should be templatized so I can use user-defined integral types with it)
And here's make_range():
template<typename T>
range<T> make_range(T const & start, T const & end)
{
return range<T>(start, end);
}
See also:
http://groups.google.com/group/boost-list/browse_thread/thread/3e11117be9639bd
and:
https://svn.boost.org/trac/boost/ticket/3469
which propose similar solutions.
And I've just found boost::integer_range; with the above example, the code would look like:
using namespace boost;
std::vector<int> vec;
// ... fill the vector ...
BOOST_FOREACH(size_t idx, make_integer_range(0, vec.size()))
{
// ... do some stuff ...
}
C++ 20's ranges header has iota_view which does this:
#include <ranges>
#include <vector>
#include <iostream>
int main()
{
for (int i : std::views::iota{1, 10})
std::cout << i << ' ';
std::cout << '\n';
for (int i : std::views::iota(1) | std::views::take(9))
std::cout << i << ' ';
}
Output:
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
Since we don't really know what you actually want to use this for, I'm assuming your test case is representative. And then plain simple for loops are a whole lot simpler and more readable:
int main() {
for (int i = 0; i <= 6; ++i){
for (int j = 3; j <= 10; j += 3){
std::cout << i << " " << j << "\n";
}
}
}
A C++ programmer can walk in from the street and understand this function without having to look up complex classes elsewhere. And it's 5 lines instead of your 60. Of course if you have 400 loops exactly like these, then yes, you'd save some effort by using your range object. Or you could just wrap these two loops inside a helper function, and call that whenever you needed.
We don't really have enough information to say what's wrong with simple for loops, or what would be a suitable replacement. The loops here solve your problem with far less complexity and far fewer lines of code than your sample implementation. If this is a bad solution, tell us your requirements (as in what problem you need to solve, rather than "I want python-style loops in C++")
Keep it simple, make a stupid macro;
#define for_range(VARNAME, START, STOP, INCREMENT) \
for(int VARNAME = START, int STOP_ = STOP, INCREMENT_ = INCREMENT; VARNAME != STOP_; VARNAME += INCREMENT_)
and use as;
for_range(i, 10, 5, -1)
cout << i << endl;
You're trying to bring a python idiom into C++. That's unncessary. Use
for(int i=initVal;i<range;i+=increment)
{
/*loop body*/
}
to achieve this. In Python, the for(i in xrange(init, rng, increment)) form is necessary because Python doesn't provide a simple for loop, only a for-each type construct. So you can iterate only over a sequence or a generator. This is simply unnecessary and almost certainly bad practice in a language that provides a for(;;) syntax.
EDIT: As a completely non-recommended aside, the closest I can get to the for i xrange(first, last, inc) syntax in C++ is:
#include <cstdio>
using namespace std;
int xrange(unsigned int last, unsigned int first=0, unsigned int inc=1)
{
static int i = first;
return (i<last)?i+=inc:i=0;
}
int main()
{
while(int i=xrange(10, 0, 1))
printf("in loop at i=%d\n",i);
}
Not that while this loops the correct number of times, i varies from first+inc to last and NOT first to last-inc as in Python. Also, the function can only work reliably with unsigned values, as when i==0, the while loop will exit. Do not use this function. I only added this code here to demonstrate that something of the sort is indeed possible. There are also several other caveats and gotchas (the code won't really work for first!=0 on subsequent function calls, for example)