65

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.

nonagon
  • 3,271
  • 1
  • 29
  • 42
  • @aaronman, Never mind, I missed the point. I don't know whether it's possible without a `(void)x;` or similar. – chris Jul 17 '13 at 22:57
  • I can't really think of any construction that would be helpfully simpler or easier to read than just the straight-forward loop you wrote. If the purpose is to do something a certain number of times, then it's OK to expose that number, as well as the counter that carries the state of "how much is left". – Kerrek SB Jul 17 '13 at 22:58
  • @chris you know what I actually missed the point too – aaronman Jul 17 '13 at 22:59
  • You need to provid more information on what are you going to do inside this loop – Ran Eldan Jul 17 '13 at 23:01
  • 2
    To maybe clear this up, my interpretation is that the OP is trying to do this with a range-based for loop. However, something like `for (auto x : boost::irange(0, 10)) f();` will give a warning because `x` is unused. – chris Jul 17 '13 at 23:04
  • 9
    But the variable _is_ referenced (in the loop condition). The compiler should definitely not emit a warning for that code. – rodrigo Jul 17 '13 at 23:05
  • 4
    @rodrigo: I don't think so, the loop condition uses a hidden iterator. `x` is written to but never read, causing compilers to emit warnings. – Mooing Duck Jul 17 '13 at 23:10
  • 2
    @MooingDuck `x<10;` reads `x`? – Yakk - Adam Nevraumont Jul 17 '13 at 23:34
  • @Yakk: whoops, I was looking at chris' boost comment, where he also mentioned a warning. That explains the confusion. – Mooing Duck Jul 17 '13 at 23:47
  • Sorry about the confusion - I was indeed looking for a better way to write the for loop. I've edited the question to make this more clear. – nonagon Jul 18 '13 at 17:51
  • 1
    @Yakk: `for (auto x : boost::irange(0, 10))` does not contain the expression `x<10`. It contains a statement more like `auto x = *__secret_iterator1;`. – aschepler Jul 18 '13 at 17:57
  • *whoosh* sorry, misplaced the thread of discussion. Now if only we where allowed to drop the variable `x` `for( : boost::irange(0,10) )` or `for( auto : boost::irange(0,10) )` or even `for( boost::irange(0,10) )`. – Yakk - Adam Nevraumont Jul 18 '13 at 18:25
  • In this case I just do: `boost::for_each(boost::irange(0,10), [&](int) { ... });`. Hope that helps. – gnzlbg Mar 19 '14 at 13:36
  • That the `x` can't be omitted is a tragedy. Complete inconsistency with the rest of the language. Silly and sign of systemically wrong approach to these new features! – Lightness Races in Orbit Oct 16 '15 at 18:15

11 Answers11

47

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

sehe
  • 374,641
  • 47
  • 450
  • 633
23

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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    Only answer so far that actually deserves upvotes. Might be worth mentioning that the C++11 spec says the loop variable (the "for-range-declaration") is mandatory. – Nemo Jul 17 '13 at 23:16
  • I would say that compiler warning emitted for this is just plain broken. –  Jul 18 '13 at 19:43
  • 15
    I think, it's dangerous to actually advise people to ignore compiler warnings. That leads to programs compiling with a ton of (ignored) warnings which hide the occasional wanted, important and helpful warning. I would never accept a patch to my project that does not compile without a single warning for precisely that reason... – cmaster - reinstate monica Jul 18 '13 at 22:15
  • 2
    @cmaster, I'm not advising them to blindly ignore all warnings, just _that_ warning at _that_ point in the code. It's fine to do so once you understand the warning and realise you know better than the compiler. Since it may well have been my use of the word 'any' which caused you to think I meant 'all' rather than zero-or-one as intended, I've removed it :-) – paxdiablo Jul 18 '13 at 22:35
  • 5
    It's hard to ignore *some* warnings. That means you have to remember it as long as you maintain the code, if a new developer comes, he has to understand that the warning is harmless... I would definitely use some kind of `UNUSED(var)` macro to supress the warning. – Karoly Horvath Jul 20 '13 at 11:51
  • @Karoly, yes, if you've got something like `#warning` available to you, you can output a warning to ignore the warning :-) but you're right, it's probably better to suppress it with a `#pragma` or some sort of macro that uses the variable. – paxdiablo Jul 20 '13 at 12:56
9

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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 2
    Why is 1 special-cased? Seems unnecessary – Mooing Duck Jul 17 '13 at 23:11
  • Obviously the best answer, no branching – aaronman Jul 17 '13 at 23:11
  • 8
    Why all the template magic? My answer is 3 lines and will also compile down to fully unrolled loop for compile-time constant `n`... (Recently verified on gcc/clang) – sehe Jul 17 '13 at 23:12
  • @MooingDuck I call the rvalue `operator()` for `1`. So if you happen to pass in a class that does something special when the functor is no longer going to be used, and you call it with a temporary functor, I behave optimally. And yes, that is ridiculous. – Yakk - Adam Nevraumont Jul 17 '13 at 23:12
  • 1
    @sehe Who trusts optimizers? But seriously, the OP asked to eliminate the loop variable: so I took the OP seriously. In practice, you should just suppress the warning and do what the OP wrote. Plus, I carefully call `operator()&&` on the passed in functor, in case the functor optimizes that case. (note: nobody optimizes that case) – Yakk - Adam Nevraumont Jul 17 '13 at 23:15
  • 1
    @Yakk +1 for that comment. – sehe Jul 17 '13 at 23:17
  • 2
    Just by looking at the code I could tell that it was written by you. – David G Jul 17 '13 at 23:23
  • 1
    @0x499602D2 look again: I made it *worse*! – Yakk - Adam Nevraumont Jul 17 '13 at 23:31
  • 5
    These are horrifying to look at! That's a ridiculous amount of code, all to avoid a compiler warning which shouldn't occur in the first place. I mean, this is impressive in a code-golf kind of way, but there's no way I'd consider using anything like these in production code. The amount of time most people would spend trying to figure out what the heck is going on is just not worth whatever abstract gain there might be for not having an iteration variable. – Darrel Hoffman Jul 18 '13 at 05:55
  • 1
    @DarrelHoffman Sure, but can you figure out a way to iterate a function 10 times *without using a variable* in a cleaner way? It is a tricky problem. I included my failed attempts in case someone can figure out a way to "improve" them. (Hmm, you could probably do it using boost preprocessor macros as well...) I agree that one should just suppress the warning (and included a means to do that). – Yakk - Adam Nevraumont Jul 18 '13 at 14:08
9

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.

David G
  • 94,763
  • 41
  • 167
  • 253
  • 1
    10 out of 10 for style, but beware that even changing `auto x` to `const auto& x` will not stop Clang 3.0 from generating `subq %rsp, $hugenumber ... callq memset` in addition to the nice loop. So it's probably a bad idea to use this approach with `range<100000>` until the compiler support catches up. – Quuxplusone Dec 04 '13 at 21:32
5

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.

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85
4

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();
Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
  • `for(unsigned x{},x<10u;++x)` is arguably more elegant – Mooing Duck Jul 17 '13 at 23:17
  • @MooingDuck `for(int x{};x<10;++x)` saves characters! – Yakk - Adam Nevraumont Jul 17 '13 at 23:19
  • @Yakk: We should post a question seeing if we can make that syntax more elegant still. – Mooing Duck Jul 17 '13 at 23:20
  • @MooingDuck I disagree. 1. It won't even compile since you have a typo in it. 2. There is no support for such brace-initialization in the current MSVC release. Sacrificing portability for 2 characters is not elegant. – Pixelchemist Jul 17 '13 at 23:29
  • Portability and elegance are orthogonal IMO. There's lots of elegant code that isn't portable. Also, the code is _theoretically_ portable, it simply doesn't work on compilers who are non-confirming in this area. In the real world, I totally see your point, and would never do what I typed. – Mooing Duck Jul 17 '13 at 23:35
4

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.

evnu
  • 6,450
  • 2
  • 27
  • 38
4

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.

Community
  • 1
  • 1
  • 1
    I think at that point I'd rather just write out the for loop without the range-based syntax (i.e. for(uint x=0; x – nonagon Feb 19 '14 at 18:22
2

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.

atb
  • 1,412
  • 1
  • 14
  • 30
1

Will this work for you?

for([[maybe_unused]] int x = 0; x < 10; ++x ) {
    /* compiles without warnings */
}

or

for ([[maybe_unused]] auto x : std::views::iota (0, 10))
{
    /* compiles without warnings */
}

https://en.cppreference.com/w/cpp/language/attributes/maybe_unused

Catriel
  • 461
  • 3
  • 11
0

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;
}
Gergely Nagy
  • 73
  • 1
  • 5