7

I have a C++ container and I want to run a loop the same number of times as there are elements in that container. But I do not care about the values in the container during the loop. For example:

for (const auto& dummy : input) {
    cout << '.';
}

The only problem is, dummy is an unused variable and I have instructed the compiler to prohibit those.

Two inelegant solutions I have come up with are to say (void)dummy; in the loop body to silence the compiler, or to use an old-style for loop from 0 to distance(begin(input), end(input)).

I tried omitting the variable name but that failed to compile (no big surprise).

I'm using GCC 4.7.2.

14
  • 3
    why do you want to loop container and ignore it's content? what are you trying to archive? Commented Apr 4, 2014 at 8:08
  • 1
    @bilz, I suspect the number of items is what is pertinent (and the number of times it must be performed) rather than the actual content and that OP wants to use auto-iterators rather than the usual for loop. Commented Apr 4, 2014 at 8:11
  • 2
    If that's the case, what he wants is just to get input.size() Commented Apr 4, 2014 at 8:12
  • 1
    Range for is used to iterate every elements in a container. If you don't access elements, why do you use range for? For example, no one uses a while and execute exactly fixed one time in block instead of using if statement. Commented Apr 4, 2014 at 8:24
  • 6
    culinary.SE - So, I have this bowl of soup. How do I use a knife to eat it? Commented Apr 4, 2014 at 8:33

7 Answers 7

5

No need for explicit loops.

use std::begin;
use std::end;
std::cout << std::string(std::distance(begin(input), end(input)), '.');

Or in non-generic context:

std::cout << std::string(input.size(), '.');

If you want to do something more complicated in the loop, just go with (void)dummy;, really. It's clear, well-known and works.

Also look at <algorithm>; what you are implementing may be better implemented in terms of those functions. C++ Seasoning is a nice talk about this.

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

3 Comments

Because input.size() is too simple?
OK, fair enough. I should stop meddling with underspecified questions.
@rubenvb: I did write in the OP that I could use std::distance. Here it has been inferred that I actually wanted to know "how can I write a series of N dots" which is not what I asked. I apologize for not writing a more sophisticated bit of logic inside the for loop.
3

As all decent suggestions are only comments (some belonging to deleted answers by now), I'll gather them here. Bottom line is: you shouldn't be looping at all.

Instead, use the size of the container and do whatever you want to do that many times. Proposed variants of the code you gave:

cout << string(input.size(), '.'); // @Matt
std::fill_n(std::ostream_iterator<char>(std::cout), input.size(), '.'); // @Hilborn

And the ones that still loop (not recommended:

std::for_each(input.begin(), input.end(), [](type) { std::cout << '.'; }); // @Rapptz
std::transform(input.begin(), input.end(), std::ostream_iterator<char>(std::cout),
[] (auto&&) { return '.'; }); // @hilborn

Hint: pick the first one. It's not only the shortest, it expresses what you are doing in the clearest way possible.

5 Comments

I doubt he actually wants to print a '.' for every entry..... Something more involved that he omitted for this minimal working example is much more likely.
@example That is not mentioned anywhere in the question.
He clearly states that his intent is to have a loop and gives the source as an example.
@example "to have a loop" is not his intent. The only thing that is clear is that the OP wants to execute something N times.
@example: You are correct: printing a dot N times is not the "meat" of my question. Let's just say that "something" needs to be done N times, such as calling a function with a fixed set of arguments. For that reason, std::for_each with the lambda is the best solution that wasn't in my original question, I think.
2

If the operation that you actually want to perform is the trivial one that you wrote in your example, a much better way to achieve what you want is:

std::cout << std::string{boost::distance(input), '.'};

Otherwise, if you just want to loop over a range and perform an operation for each element, ignoring the element value, the for_each <algorithm> is exactly what you want.

  • Using boost:

    #include <boost/range/algorithm.hpp>
    boost::for_each(input, [](auto&& /*ignored*/) { /* do stuff */; });
    
  • Using the STL:

    #include <algorithm>
    std::for_each(std::begin(input), std::end(input), [](auto&& /*ignored*/) { 
      /* do stuff */; 
    });
    

Note that within the lambda you cannot break or continue, but you can return early to achieve the same behavior.

Still, you should take a look at the other STL algorithms and choose the one that matches your intent best (it will generally also be the fastest).

3 Comments

This taught me about boost::distance(). Thanks for that.
Could I just point out that STL != stdlib? I was confused when learning C++, because people used the term STL when referring to stdlib, which is what the std namespace is. A quick look at when for_each was added to stdlib, tells me that there never was any for_each in STL. stackoverflow.com/a/5205571/7395227; en.cppreference.com/w/cpp/algorithm/for_each
The STL is the Standard Template Library and is a subset of the C++ standard library. The <algorithm> header is part of the STL, and the for_each algorithm has been a part of it since before C++ was standardized (e.g. see the SGI STL docs: martinbroadhurst.com/stl/for_each.html ).
1
for (const auto& dummy __attribute__((unused)) : input) {
    cout << '.';
}

as you are using gcc anyways. As a c++11 attribute this is written as (thanks to Jonathan Wakely)

for (const auto& dummy [[gnu::unused]] : input) {
    cout << '.';
}

5 Comments

-1: This is a compiler-specific extension, and terrible terrible style.
@rubenvb although some description for the code from the author would be appreciated, OP explicitly mentioned C++11, so I can only agree it's a bad style.
@rubenvb I honestly thought that it was an attribute in the c++11 sense as well - but turns out it is not. It is still a correct answer. It solves the states problem - very much unlike cout << string(input.size(), '.').
The C++11 equivalent would be for (const auto& dummy [[gnu::unused]] : input), which uses a vendor-specific attribute, using the gnu attribute-namespace
example: You're correct, this solves the stated problem, and taught me a new place I can put the "unused" attribute. So thank you. And thanks to @JonathanWakely for showing us the C++11 way.
0

Well, you can silence the warning with a pragma: #pragma GCC diagnostic push #pragma GCC diagnostic error "-Wunused-variable" /code here/ #pragma GCC diagnostic pop

But I urge you not to do that. Given the tradeoffs, the "(void)dummy" option is better. I also urge you not to do a dummy inline function as advized by R Sahu, as this just obfuscates your code and unnecessary complexity.

I think in this case billz and moo-juice gave good advice. You are probably better off doing this with an old style for loop.

I can think of one situation where you might want to use the options discussed here. If the container "input" is of a type where getting the size requires traversing the range, and if the code in question is going to be in a performance critical area, you may find it worthwhile to optimize out iterating over the loop twice. In that case use: (void) dummy; // silence uneccessary warning.

However this is almost certainly not the case in the example code you have posted, where io operations will dominate, making this a premature optmization.

Remember that you code will be read many more times than it will be written, so focus on what makes the code most readable. Using new C++ features is only a benefit if it bring an increase in the readability, maintainability, or flexibility of your code -- or if it brings a meaningful performance improvement.

In the case of the example you've posted, your guiding principle should be "what the easiest for a colleague to read?".

1 Comment

I cannot believe your answer got 2 downvotes in just a few hours. I actually think it's reasonable and covers some of the important bases. +1.
0
template<typename F>
void repeat(std::size_t n, F&&f){
  if(!n) return;
  while(--n) f();
  std::forward<F>(f)(); // enable rvalue operator()
}
template<class C,class=void>struct has_size:std::false_type{};
template<class C>struct has_size<C,decltype(
  std::declval<C>().size(),void()
)>:std::true_type{};
template<typename C>
std::size_t size_helper(C&&c,std::false_type){
  using std::begin; using std::end;
  return std::distance(begin(c),end(c));
}
template<typename C>
std::size_t size_helper(C&&c, std::true-type){
  return c.size();
}
template<typename C>
std::size_t size(C&&c){
  return size_helper(c, has_size<C>());
}

then:

repeat(size( source ),[&]{
  std::cout<<".";
});

should work with standard containers and arrays at nearly optimal efficiency. It does count all of the elements in a forward_list before starting the repeat code.

Comments

0

Another option is to place a call to a dummy inline function.

Define a dummy function.

template <typename T> void noop(T const& t){} 

Use the dummy function.

for (const auto& dummy : input) {
    noop(dummy);
}

2 Comments

Not sure how this is an improvement over (void)dummy with the exception that you're perhaps showing intent - but at this point we're using lots of fluffs and work arounds instead of, say, a for loop!
+1 According to Herb Sutter, (void)dummy is only partly effective (it doesn't work with EDG based compilers).

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.