C++11 range-based for loops without loop variable

C++C++11

C++ Problem Overview


In C++ I need to iterate a certain number of times, but I don't need an iteration variable. For example:

for( int x=0; x<10; ++x ) {
    /* code goes here, i do not reference "x" in this code */
}

I realize I can do this by replacing "code goes here" with a lambda or a named function, but this question is specifically about for loops.

I was hoping that C++11's range-based for loops would help:

for( auto x : boost::irange(0,10) ) {
    /* code goes here, i do not reference "x" in this code */
}

but the above gives an "unreferenced local variable" since I never explicitly reference x.

I'm wondering if there is a more elegant way to write the above for loops so that the code does not generate an "unreferenced local variable" warning.

C++ Solutions


Solution 1 - C++

Edit now with 100% fewer loop variables declared.

template <typename F>
void repeat(unsigned n, F f) {
    while (n--) f();
}

Use it as:

repeat(10, f);

or

repeat(10, [] { f(); });

or

int g(int);
repeat(10, std::bind(g, 42));

See it live at http://ideone.com/4k83TJ

Solution 2 - C++

There may be a way to do it but I very much doubt it would be more elegant. What you have in that first loop is already the correct way to do it, limiting the scope/lifetime of the loop variable.

I would simply ignore the unused variable warning (it's only an indication from the compiler that something may be wrong, after all) or use the compiler facilities (if available) to simply turn off the warning at that point.

This may be possible with some sort of #pragma depending on your environment, or some implementations allow you to do things like:

for (int x = 0; x < 10; ++x) {
    (void)x;

    // Other code goes here, that does not reference "x".
}

I've seen that void trick used for unused parameters in function bodies.

Solution 3 - C++

Assuming 10 is a compile time constant...

#include <cstddef>
#include <utility>
template<std::size_t N>
struct do_N_times_type {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
    closure();
    do_N_times_type<N-1>()(std::forward<Lambda>(closure));
  }
};
template<>
struct do_N_times_type<1> {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
    std::forward<Lambda>(closure)();
  }
};
template<>
struct do_N_times_type<0> {
  template<typename Lambda>
  void operator()( Lambda&& closure ) const {
  }
};

template<std::size_t N, typename Lambda>
void do_N_times( Lambda&& closure ) {
  do_N_times_type<N>()( std::forward<Lambda>(closure) );
};
#include <iostream>
void f() {
  std::cout << "did it!\n";
}
int main() {
  do_N_times<10>([&]{
    f();
  });
}

or just

int main() {
  do_N_times<10>(f);
}

Other ridiculous methods:

Write a range iterator (I call mine index) that produces an range of iterator-on-integral types (I default to std::size_t). Then type:

for( auto _:index_range(10) )

which uses a variable (_) but looks exceedingly confusing.

Another crazy approach would be to create a python-like generator. Writing a generator wrapper that takes an iterable range and produces a function that returns std::optional on the value_type of the range isn't tricky.

We can then do:

auto _ = make_generator( index_range(10) );
while(_()) {
}

which creates a temporary variable as well, and is even more obtuse.

We could write a looping function that operates on generators:

template<typename Generator, typename Lambda>
void While( Generator&& g, Lambda&& l ) {
  while(true) {
    auto opt = g();
    if (!opt) return;
    l(*opt);
  }
}

which we then call like:

While( make_generator( index_range(10) ), [&](auto&&){
  f();
});

but this both creates some temporary variables in the function, and is more ridiculous than the last, and relies on features of C++1y which has not even been finalized.

Those where my attempts to create a variable-less way to repeat something 10 times.

But really, I'd just do the loop.

You can almost certainly block the warning by typing x=x;

Or write a function

template<typename Unused>
void unused( Unused&& ) {}

and call unused(x); -- the variable x is used, and its name is dropped inside, so the compiler may not warn you about it inside.

So try this:

template<typename Unused>
void unused( Unused&& ) {}
for(int x{};x<10;++x) {
  unused(x);
  f();
}

which should suppress the warning, and be actually easy to understand.

Solution 4 - C++

There actually is a way to make this work. All you need to do is return an std::array with the length specified by the constant you provide:

template <int N>
using range = std::array<int, N>;
 
int main()
{
    for (auto x : range<5>())
    {
        std::cout << "Awesome\n";
    }
}

Output:

> Awesome
> Awesome
> Awesome
> Awesome
> Awesome

Here is a demo.

Note: This is assuming the range specifier is a compile-time constant, so if you have to use a variable make sure it is validly marked constexpr.

Solution 5 - C++

In my opinion you misuse the range-based loop. The range-based loop should be used when the logic is: "for each element in the collection do something". The whole idea is to get rid of the index variable since it isn't important. If you have a collection, you should instrument it with the necessary APIs to enable range-based iteration. If you don't have a collection, you have no business to use range-based loop (actually, that's what the compiler implies in not-so-informative way). In this situation a normal for/while loop is the natural choice.

Solution 6 - C++

There isn't any way to make a range based for work for simply iterating over several numbers.

C++11 range-based loops require a ranged expression which may be:

  • an array or
  • a class having either
  • Member functions begin() and end() or
  • available free functions begin() and end() (via ADL)

In addition to that: A range based for produces some overhead:

for ( for_range_declaration : expression ) statement

expands to

range_init = (expression)
{
  auto && __range = range_init;
  for ( auto __begin = begin_expr,
  __end = end_expr;
  __begin != __end;
  ++__begin ) {
    for_range_declaration = *__begin;
    statement;
  }
}

Where begin_expr and end_expr are obtained via array inspection or begin() / end() pairs.

I don't think this gets any "cleaner" than a simple for-loop. Especially with respect to performance. No calls, just a plain loop.

The only way I can figure out to make it more elegant (where elegant is clearly subject to my opinion) is by using a size or unsigned type here:

for(size_t x(0U); x<10U; ++x) f();

Solution 7 - C++

Already best answered in https://stackoverflow.com/a/21800058/1147505 : how to define an UNUSED macro to be used throughout your codebase, which suppresses this warning. In a portable way.

Solution 8 - C++

You could use the STL together with a lambda expression.

#include <algorithm>
#include <iostream>

int main() {
    int a[] = {1,2,3,4,5,6};

    std::for_each(std::begin(a),
            std::end(a),
            [](int){std::cout << "Don't care" << std::endl;});
}

This approach also works for arbitrary containers such as vectors or lists. Let vector<int> a, then you'd call a.begin() and a.end(). Note that you can also use a function pointer instead of a lambda expression.

The above preserves your notion of using a foreach, while not complaining about an unused parameter.

Solution 9 - C++

this works in GCC and clang and any compiler that supports the gnu attributes:

for( [[gnu::unused]] auto x : boost::irange(0,10) ) {

and should compile in any c++11 compiler but may not suppress the warning if the compiler doesn't recognise the gnu attributes.

Solution 10 - C++

To join the contest:

#include <iostream>
#include <boost/range/irange.hpp>
using namespace std;
using namespace boost;

namespace {
    template<typename T> inline void 
    unused(const T&) {}
}

int main() {
    for (auto&& i : irange(0,10)) {
        unused(i);
        cout << "Something" << endl;
    }
    return 0;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnonagonView Question on Stackoverflow
Solution 1 - C++seheView Answer on Stackoverflow
Solution 2 - C++paxdiabloView Answer on Stackoverflow
Solution 3 - C++Yakk - Adam NevraumontView Answer on Stackoverflow
Solution 4 - C++David GView Answer on Stackoverflow
Solution 5 - C++SomeWittyUsernameView Answer on Stackoverflow
Solution 6 - C++PixelchemistView Answer on Stackoverflow
Solution 7 - C++user1147505View Answer on Stackoverflow
Solution 8 - C++evnuView Answer on Stackoverflow
Solution 9 - C++atbView Answer on Stackoverflow
Solution 10 - C++Gergely NagyView Answer on Stackoverflow