66

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.

14
  • 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. Commented Jul 17, 2013 at 23:04
  • 9
    But the variable is referenced (in the loop condition). The compiler should definitely not emit a warning for that code. Commented Jul 17, 2013 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. Commented Jul 17, 2013 at 23:10
  • 2
    @MooingDuck x<10; reads x? Commented Jul 17, 2013 at 23:34
  • 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;. Commented Jul 18, 2013 at 17:57

11 Answers 11

48

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

Sign up to request clarification or add additional context in comments.

9 Comments

@chris it depends, do you want the code to compile? Or do anything? Both of those would be improved by operating on an instance of the lambda. :)
@Yakk, I get the feeling I just missed the edit or read it improperly.
@chris I had F() instead of f() inside the function template. Fixed now
@sehe you can also drop the () in []{ f(); }, and I'd stuff a & in the [] so that it behaves like a sub-scope.
@Yakk I will leave that to the actual user. All I wanted to show is you can pass a lambda instance, bind expression, or, indeed, any calleable.
|
24

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.

6 Comments

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.
I would say that compiler warning emitted for this is just plain broken.
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, 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 :-)
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.
|
10

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.

10 Comments

Why is 1 special-cased? Seems unnecessary
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 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)
Just by looking at the code I could tell that it was written by you.
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.
|
10

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.

1 Comment

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.
7

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.

Comments

6

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.

1 Comment

nice, clean, idiomatic
5

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();

5 Comments

for(unsigned x{},x<10u;++x) is arguably more elegant
@MooingDuck for(int x{};x<10;++x) saves characters!
@Yakk: We should post a question seeing if we can make that syntax more elegant still.
@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.
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.
5

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.

1 Comment

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<N; ++x)). It's unfortunate - the range-based syntax is so clean & concise, I really wish it supported this use case more directly!
3

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.

Comments

3

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

Comments

1

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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.