12

I've effectively got the following problem: I want to be able to build with -Wall -Wextra -Werror, however, the following code will complain about unused parameters:

struct foo
{
    template <typename... Args>
    static void bar()
    { }

    template <typename T, typename ... Args>
    static void bar(T&& value, Args&& ... args)
    {
    #ifdef DEBUG
        std::cout << value;
        bar(std::forward<Args>(args)...);
    #endif
    }
};

The first unused parameter is easy to fix:

    #ifdef DEBUG
        std::cout << value;
        bar(std::forward<Args>(args)...);
    #else // Shut the compiler up
        (void) value;
    #endif

My question is, how can I do this with the remaining args? Neither

(void)(args...);

Nor

(void)(args)...;

will work, both complain about the parameter pack not being expanded.

(This is under GCC 4.7.3, if that will make any difference to a potential solution).

Yuushi
  • 25,132
  • 7
  • 63
  • 81

3 Answers3

12

When working with variadic template, it is more clean to use sink:

struct sink { template<typename ...Args> sink(Args const & ... ) {} };


#ifdef DEBUG
    std::cout << value;
    bar(std::forward<Args>(args)...);
#else 
    sink { value, args ... }; //eat all unused arguments!
#endif
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • This is likely the solution I will go with, but I'm still somewhat curious as to why the code in the question doesn't work. – Yuushi Oct 23 '13 at 04:10
  • 1
    @Yuushi: There are certain contexts where variadic arguments can unpack themselves. In your context they don't unpack. See [this topic](http://stackoverflow.com/questions/17652412/what-are-the-rules-for-the-token-in-the-context-of-variadic-template). – Nawaz Oct 23 '13 at 04:11
  • Just goes to show how naive I am. I would have just declared `static bar() {}` non-template, and moved the `bar` forwarder below the `#endif` in the template-version. Works with pedantic warnings on my Mac (clang), but I like your solution better. – WhozCraig Oct 23 '13 at 04:18
  • @Nawaz, even though sink properly eats all unused arguments, i guess it still makes a call to copy ctor for sink. will it become nop in a released build or it will still created as auto stack variable. Sorry I am a bit new to cpp – Hasan Emrah Süngü Mar 11 '21 at 14:52
  • @HasanEmrahSüngü: Good question. :thumbs-up. `sink`'s constructor takes all arguments by reference, so none of the unused arguments you passed to it would make any copy! Also, I'm pretty much sure if you use `-O2` or higher, it'd be `nop`. – Nawaz Mar 11 '21 at 15:07
  • @Nawaz, thank you very much for your super fast answer on a pretty old question. Yeah I have seen the arguments being passed as `const &`. My copy question is on the `sink` object being actually created then, of course, auto destroyed when it is out of scope. But since you mention that `Also, I'm pretty much sure if you use -O2 or higher, it'd be nop.` I guess sink will be nop – Hasan Emrah Süngü Mar 11 '21 at 15:09
  • @HasanEmrahSüngü: ah, I see. Yes. `sink` creation would be optimized away by the compiler. I think it'd be optimized at `-O1` itself, as it does not have any IO. The compiler will quickly optimize it. `-O2` or higher is more complicated cases. – Nawaz Mar 11 '21 at 16:00
1

You could really go with conditional naming here.

#ifdef DEBUG
#define DEBUG_NAME(x) x
#else
#define DEBUG_NAME(x)
#endif

static void bar(T&& DEBUG_NAME(value), Args&& DEBUG_NAME(args)) {}
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
0

Another way to solve this issue would be to move the #define to create an alternate template which omits the argument name:

#ifdef DEBUG
    template <typename T, typename ... Args>
    static void bar(T&& value, Args&& ... args)
    {
        std::cout << value;
        bar(std::forward<Args>(args)...);
    }
#else 
    template <typename T, typename ... Args>
    static void bar(T&& value, Args&& ...)
    {
    }
#endif