15

Is there any way to not "use" the loop variable in a range-based for loop, but also avoid compiler warnings about it being unused?

For context, I'm trying to do something like the following. I have "treat warnings as errors" enabled, and I'd rather not do a hack like forcing the variable to be "used" by pointlessly mentioning it somewhere.

size_t getSize(const std::forward_list &list)
{
  size_t count = 0;
  for (auto & : list) // compile error, but if i do "auto &i" here, MSVC
                      // complains (reasonably) that i is unused
  {
    ++count;
  }
  return count;
}

I know there are other ways to do this, but let's say for argument's sake that I need to use a range-based for loop.

manlio
  • 18,345
  • 14
  • 76
  • 126
Karu
  • 4,512
  • 4
  • 30
  • 31
  • 3
    Why not `list.size()`, or at least `std::distance(list.begin(), list.end())`? – Kerrek SB Feb 15 '14 at 01:43
  • @0x499602D2 OK... that's what I meant by "pointlessly mentioning it somewhere". Why `(void)x;` rather than just `x;`? – Karu Feb 15 '14 at 01:43
  • @KerrekSB See the last sentence - this is a synthetic example about the range-based for loop syntax. I used `std::forward_list` specifically because it doesn't have `size()`, but yeah, not a perfect example. – Karu Feb 15 '14 at 01:43
  • @Karu Because you'll still get a warning that it's unused (at least on my compiler). – David G Feb 15 '14 at 01:45
  • 1
    http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem – Mooing Duck Feb 15 '14 at 01:47
  • 2
    @Karu: I'd go as far as call it "symptomatic": The reason your code feels odd is because it's inappropriate, and there are better ways to achieve what you want. For example, `distance` works just fine for forward lists and does exactly what it should. I bet that whatever you're really trying to do can similarly be done better with a more idiomatic solution. – Kerrek SB Feb 15 '14 at 01:47
  • 2
    Tell us _why_ you need this, and we'll tell you how to solve _that_ problem instead – Mooing Duck Feb 15 '14 at 01:47
  • 2
    @MooingDuck I 'need' this simply because I want to understand the C++ language. It's not a practical problem; I just thought it was odd that you can omit function parameters (for example) but not range-based for loop variable names, and I wanted to discover more about it. Is there a better way to phrase such questions? – Karu Feb 15 '14 at 01:49
  • @Karu: This just doesn't feel like a valid stackoverflow question to me, it feels off topic. Unfortunately, it doesn't quite fit under any of the "do not ask" categories, so I don't have rules to back my feeling. – Mooing Duck Feb 15 '14 at 01:54
  • A range-for is not like a function, you don't have the option of omitting the variable name. You should implement what the other comment suggested if you're looking for the best way in your situation. – David G Feb 15 '14 at 02:09
  • The C++ grammar simply does not allow you to omit the name. You can always just `(void)name;` the variable to explicitly tell the compiler to shut up. – Xeo Feb 15 '14 at 04:30
  • The reason you can omit function parameters is that knowing the function prototype, you might not need a parameter for a specific implementation. Range-based for loops, on the other hand, only exist at the implementation level, so there's no reason that you should be able to omit it. – zneak Feb 15 '14 at 04:40

5 Answers5

7

You can define a macro:

#if defined(__GNUC__)
#  define UNUSED __attribute__ ((unused))
#elif defined(_MSC_VER)
#  define UNUSED __pragma(warning(suppress:4100))
#else
#  define UNUSED
#endif

...
for (auto &dummy UNUSED : list)
{
  ++count;
}
...

It works well with GCC and CLANG (not so sure about MSVC... I seem to remember that MSVC will disable the warning for the rest of the file).

Also:

template<class T> void unused(const T &) {}
...
for (auto &dummy : list)
{
  unused(dummy);

  ++count;
}
...

works on all compilers and shouldn't have any overhead (Mailbag: Shutting up compiler warnings).

The Boost header <boost/core/ignore_unused.hpp> (Boost >= 1.56) defines, for the same purpose, the function template boost::ignore_unused().

With C++11 also std::ignore is a good choice:

{
  std::ignore = dummy;
  // ...
}

Similar questions:


PS C++17 seems to be getting a [[maybe_unused]] attribute to provide a standard way of declaring an unused variable.

Community
  • 1
  • 1
manlio
  • 18,345
  • 14
  • 76
  • 126
  • I like the template function, makes it explicitly clear the variable is being ignored on purpose. – Nick May 12 '15 at 16:12
  • 1
    @david.pfx you don't need `#pragma warning restore`, there isn't such thing actually. [Documentation](https://msdn.microsoft.com/en-us/library/2c8f766e.aspx) says that `suppress` **pushes the current state of the pragma on the stack, disables the specified warning for the next line, and then pops the warning stack so that the pragma state is reset.** – izogfif Jun 29 '17 at 07:18
  • @izogfif: looks useful, but I don't see that it completely replaces restore. – david.pfx Jun 30 '17 at 14:11
  • 1
    @david.pfx writing `#pragma warning(suppress:4244) | short k = 2.0;` is equivalent to `#pragma warning(push) | #pragma warning(disable:4244) | short k = 2.0; | #pragma warning(pop)` ("|" denote newlines since there aren't newlines in comments). [Documentation](https://msdn.microsoft.com/en-us/library/2c8f766e.aspx#code-snippet-5) says **At the end of this code, pop restores the state of every warning to what it was at the start of the code.** – izogfif Jul 03 '17 at 17:59
  • @izogfif: of course, that seems obvious. But consider the need to suppress some warning for an entire header file and restore it at the end. How would you do that without restore? – david.pfx Jul 05 '17 at 00:32
  • @david.pfx you would add `#pragma warning(push) | #pragma warning(disable: 4100)` at the start and `#pragma warning(pop)` at the end. `pop` will restore stack of the settings of warnings to the state which it had before previous `push`. In short, to restore you first save via `push` and then restore via `pop`. – izogfif Jul 05 '17 at 07:43
5

You can always state explicitly that the variable is guaranteed unused in the loop body:

ptrdiff_t size( std::forward_list const& list )
{
    ptrdiff_t count = 0;
    for( auto& dummy : list ) 
    {
        (void) dummy; struct dummy;    // Wrap this in a macro if you want.
        // Here everybody including compiler knows that dummy isn't used and can't be used.

        ++count;
    }
    return count;
}

The above is however much less clear than simply using an ordinary for-loop.

Not to mention simply calling size.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • +1 for "struct dummy;" require developer to remove this macro, if the variable usage added later. – ZijingWu Feb 15 '14 at 13:32
  • 1
    What is the purpose of `struct dummy`? – qdii Feb 16 '14 at 09:52
  • 2
    @qdi: With `struct dummy` the name `dummy` refers to an incomplete type, so most any use of the *variable* `dummy` is invalid and causes a compilation error. As a practical measure it works nicely. Some unnatural statements such as `dummy*x;` might still slip through the net, and in the same vein there is the issue of a possible user-defined conversion to `void` for the original variable (not sure of the details here, just reporting the results my association circuit throws out), but nothing's prefect, not even prefect ofrwading. – Cheers and hth. - Alf Feb 16 '14 at 14:58
4

I think that for this reason use std::for_each, like that:

template<typename T>
std::size_t  get_size(std::forward_list<T> const& list)
{
     std::size_t count = 0;
     std::for_each(begin(list), end(list), [&count](T const& ){++count;} );
     return count;
}

But, if you would get size any container, use std::distance

   std::size_t count = std::distance(begin(list), end(list) );
Khurshid
  • 2,654
  • 2
  • 21
  • 29
2

I saw that you tagged your question with c++11, but if you plan to switch to c++17 some day you could use the maybe_unused attribute for the described scenario:

for( const auto &[[maybe_unused]] item : *obj->items() )
    Foo( ... );
Andreas
  • 5,393
  • 9
  • 44
  • 53
0

One option is to exploit the fact that compilers generally don't warn about unused variables when they have non-trivial destructors, and write a generic templated wrapper to eat the actual values you are iterating over, and return dummy objects. Something like this:

template <class RangeType>
class UnusedRange {
public:
  UnusedRange(RangeType& wrapped_range) : wrapped_range_(wrapped_range) {}
  // Explicit destructor makes compiler not complain about unused vars.
  class UnusedVal { public: ~UnusedVal() {} };
  class Iterator {
  public:
    typedef decltype(((RangeType*)nullptr)->begin()) WrappedIteratorType;
    Iterator(WrappedIteratorType wrapped_it) : wrapped_it_(wrapped_it) {}
    const Iterator& operator++() { ++wrapped_it_; return *this; }
    bool operator!=(const Iterator& other) { return wrapped_it_ != other.wrapped_it_; }
    UnusedVal operator*() { return UnusedVal(); }
  private:
    WrappedIteratorType wrapped_it_;
  };
  Iterator begin() { return Iterator(wrapped_range_.begin()); }
  Iterator end() { return Iterator(wrapped_range_.end()); }
private:
  RangeType& wrapped_range_;
};
template <class RangeType>
UnusedRange<RangeType> Unused(RangeType& range) {
  return UnusedRange<RangeType>(range);
}

You can use it like this:

for (auto unused : Unused(foo)) { ... }
ioctl
  • 136
  • 4