19

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.

12
  • 3
    Why not list.size(), or at least std::distance(list.begin(), list.end())? Commented Feb 15, 2014 at 1:43
  • 1
    meta.stackexchange.com/questions/66377/what-is-the-xy-problem Commented Feb 15, 2014 at 1: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. Commented Feb 15, 2014 at 1:47
  • 2
    Tell us why you need this, and we'll tell you how to solve that problem instead Commented Feb 15, 2014 at 1: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? Commented Feb 15, 2014 at 1:49

5 Answers 5

8

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.

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

6 Comments

I like the template function, makes it explicitly clear the variable is being ignored on purpose.
@david.pfx you don't need #pragma warning restore, there isn't such thing actually. Documentation 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: looks useful, but I don't see that it completely replaces restore.
@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 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: 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?
|
6

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.

3 Comments

+1 for "struct dummy;" require developer to remove this macro, if the variable usage added later.
What is the purpose of struct dummy?
@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.
5

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

Comments

3

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

1 Comment

@XPhyro The C++17 standard doesn't define a fixed position for this attribute. Even const auto & item [[maybe_unused]] compiles in VS 2022 with /W4. It's more a matter of preference. I prefer to have attributes close to the variable, rather than to the type which might be clearer if you read it aloud... "maybe unsued item" or "item maybe unused" (if read right to left) sounds more natural than "maybe unused constant auto item", at least imho. :
1

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)) { ... }

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.