Mailbag: Shutting up compiler warnings

I recently received the following reader question (slightly edited):

About the (Stroustrup) approach of implementing IsDerivedFrom at page 27 in your book More Exceptional C++: […] why the second pointer assignment in:

static void Constraints(D* p)
{
B* pb=p; // okay, D better inherit from B…
pb=p; // huh? why this again?
}

Isn’t the initialization ” B* pb=p ” enough? What does the second assignment bring to the picture?

Excellent question. Interestingly, here’s the exact text from the book, which contains an additional comment that was intended to answer this specific question (emphasis added):

static void Constraints(D* p)
{
B* pb = p;
pb = p; // suppress warnings about unused variables
}

The reason for the redundant assignment is to try to shut up some compilers that issue warnings about unused variables. In a normal function, this is a helpful warning because, dollars to donuts, it’s a mistake and the programmer meant to use the variable but forgot or accidentally used some other variable instead. In this case, without the second line, pb is declared but never mentioned again, and by adding the extra line that mentions it one more time after its declaration, some compilers stop complaining.

Sometimes, however, we’re intentionally not using the variable, such as in this case where the only purpose of the function is to ensure that code that tries to convert a D* to a B* is compilable. (Note: There are other/better ways to achieve this. See the rest of Item 27 for superior approaches.)

However, in practice even the line “pb = p;” doesn’t shut compilers up very much, because many will still notice that now the second assigned value isn’t used, and will helpfully remind you that you probably didn’t mean to write an apparently useless “dead write” that is never read again. Most compilers offer a compiler-specific #pragma or other nonportable extension to silence this warning, but wouldn’t it be nice to have a portable way to suppress the warning that will work on all compilers?

Here’s a simple one-liner that works on all compilers I tried, and should not incur any overhead: Define an empty function template that takes any object by reference, but doesn’t actually do anything with it and so the empty function body should compile down to nothing. Let’s call it “ignore” – you can put it into your project namespace to avoid collisions on that common name.

  template<class T> void ignore( const T& ) { }

Note that ignore<T>’s parameter does not need a name; in fact, it had better not have a name or else you’ll get the same compiler warning as before about an unused variables, only in a new place.

Using ignore is easy. When you want to suppress the compiler warning, just write:

  static void Constraints(D* p)
  {
    B* pb = p;
    ignore(pb); // portably suppresses the warning
  }

Now all compilers I tried agree that the variable is “used” and stop complaining about pb.

[Oct 20: Updated  to add “const” on ignore<T>’s parameter to also handle rvalues.]

26 thoughts on “Mailbag: Shutting up compiler warnings

  1. @Krzysiek: I get your point. But because you referred to Yuri, Yuri referred to Herb and Herb was writing about boost’s explicit inline on a function template (which *is* redundant), I was stuck on that template topic. I didn’t realize that Yuri’s and your posts were about inlining functions in general. Mea culpa.
    Of course you are right, that the keyword in general is not redundant, but afaik it is only useful with free nontemplate function definitions inheader files.
    (And yes, default parameters are not allowed on function templates)

    Regards
    Arne

  2. @Arne
    But what if you want to place
    void f() { /* something tivial */ }
    in header file?
    I do not want to make it template since I would have to specify dummy template parameter with every function call (as far as I know function templates do not allow default template parameter values but I’m maybe wrong). The only way is to use ‘inline’ keyword which explicity states what I want to do. I just want to counterargue with statement that ‘inline’ keyword is redundant.

  3. @Michael: As stated above, the Code might have sideffets that you want to have.

    @Krzysiek: Compilers won’t complain about multiple definitions of template functions, because templates don’t violate the ODR if the multiple definitions are equivalent.

  4. Why not simply outcomment the code? It is of no use anyway, right?

    /Michael

  5. @Yuri
    I also agree inline might be required. I mean placing such short function in header files which are included many times. Omiting ‘inline’ keyword makes linker complaining about multiple definitions of such function.

  6. @Herb: explicit inline is required for free functions at least for gcc-based compilers. I can be wrong, but standard says that only class members will be inlined if they defined in class declaration.
    I saw disassembly of compiled code (with -O2) which generated empty function instantiations for template ones which were not declared as inline (they contained only prolog and epilog, but were exported to linker, and called by name from main code).

  7. @Dmitry,Kirill: Aha, you’re right. The explicit const lets you also handle temporaries. I’ll update the post to add the const.

  8. Yes, you’re right. I should have wrote that

    #define UNUSED(x)

    is ONLY usable for function formal parameters, and actually it is different “thing”, so UNUSED_PARAM would be better name…

    As for omitting unused formal param names – sometimes it is good to leave variable name, for the sake of description, etc..

    Thanks.

  9. Re: To those who mentioned using this for suppressing warnings on unused function formal parameters, you don’t actually need it for that — just do the same thing ignore() itself does, namely don’t give the parameter a name.

    I think it’s a matter of personal taste or coding style. However, I prefer to not delete parameter names. When you read code with deleted parameter names, you basically have no idea as to WHAT that parameter actually means, thus you have no idea as to WHY it is unused. Maybe it’s ultimately uninteresting for the function, or it’s just not yet supported but must be supported. Moreover, if need that parameter back, you will have to find the name somewhere and retype it.
    Sometimes I just comment unused parameters out:

    void foo(…., int /*some_flag*/, …) {…}

    However that badly cooperates with other multi-line comments – there is no nested multi-line comments.

    So I prefer to explicitly mark it as unused, it also provides a good place for comment regarding a reason why it is unused. For example:

    inline int rl_WaitForSingleObjectEx(rl_HANDLE obj, unsigned long timeout, int alertable, debug_info_param info)
    {
    (void)alertable; //not yet supported – support it!

    }

  10. @Herb: Your version does not work correctly on the code I gave. Try to compile:

    template
    void unused(T&) {}

    std::string foo() { return std::string(); }
    int bar() { return 0; }

    int main()
    {
    unused(foo());
    unused(bar());
    }

  11. A colleague emailed me privately asking:

    Why not use
    sizeof pb;
    Is there a compiler on which this doesn’t quiet the warning?

    Before trying it, my guess/expectation is that it would get the same result as just mentioning the variable name (e.g., “pb;”, see earlier comments), because it falls into the same category of using the name in an expression that has no effect. Therefore, whether or not it quiets the original “unused variable” warning, it is likely to generate a new warning saying that the expression has no effect or its result was unused.

    Then I tried it, and the result… drum roll please… was: It doesn’t suppress the warning on Borland, and just changes it to “expression has no effect” on EDG. Not exactly the same effect on every compiler as just writing a statement containing only the variable name, but it’s close.

    So the solution I posted (and Boost’s identical facility) is still the only technique that works on all the compilers I tried. Any more suggestions of variants to try?

  12. @Glen: No, Microsoft didn’t say we’d refuse to implement them, that was a mistake in the minutes that we didn’t see to ask to be corrected. I and others have been saying for two years now that trying to standardize attributes is a bad idea and fraught with peril, and I’ve still been actively saying so on the committee reflectors over the past two months.

  13. grumps corner: can’t we squeeze this into the c++0x proposal (the one with the other attribute stuff) so we can have a standard compiler switch/attribute for this stuff? ;)

    Maybe these are two I would actually vote for:

    int a [[unused]];
    int f( int a [[unused]] );

    more “creatively”:

    // Maybe this could cause all calls/side effects to trace (in the following line) to
    be ignored but allowing the calls to be left left
    in the code when [[unused]] can be removed if required later.

    void trace [[unused]] ( int x, int y);

    // Maybe this to strip out calls to and creates of trace_logger.
    class trace_logger [[unused]] { }

    // Getting silly but couldn’t a [[used]] be, er, used too?

    // Construction wanted, but no further
    // use required so compiler don’t complain
    // about unused. At least I think this error sometimes happened back when i Used to use C++ (ho ho).

    Object y[[used]](); // Prevent unused error.

    // How about?
    void used_this [[deprecated(“we used to use this but now we use_that instead”)]]( int x, y);

    I’ve asked this before but I hear MS aren’t implementing c++0x attributes? Is this true? That might be no bad thing thing because they are so overused in .NET (and just see the sillyness above!) but then C++/CLI has single bracket attributes that though I like better will just make for non standard overuse… what was the committee view in not voting for the C++/CLI type attribute?

    Others having “fun” with attributes I see:
    http://stackoverflow.com/questions/296838/c0x-attributes-youd-like-to-see

    (interesting comments too about a plan for a world with no macros and the “travesty” of MS not doing them? I’m not sure it’s a travesty but I would like to know the reasoning or if true or not.

  14. P.S.: To those who mentioned using this for suppressing warnings on unused function formal parameters, you don’t actually need it for that — just do the same thing ignore() itself does, namely don’t give the parameter a name.

  15. @Zura: I considered an UNUSED macro too, but there are several problems with that:

    1. It doesn’t work, because the macro expands to nothing during preprocessing before language rules begin. So writing UNUSED(x) is the same as writing nothing, and you end up with the warning.

    2. It doesn’t handle the case when you want an expression with side effects, such as:

      ignore( SomeFunctionWithAResult() );
    

    and you really want the contents of the ignore() to execute normally, not be discarded.

    By the way, here’s one option I didn’t present, since this wasn’t intended to be a full article, but here goes: You could simply mention the variable again, such as:

      static void Constraints(D* p)
      {
         B* pb = p;
         pb; // mention the variable again
      }
    

    Unfortunately, this also doesn’t do the trick because it does suppress the original warning on both compilers I tried (good) but creates a new warning on an overlapping set of compilers: Expression has no effect. In my test suite, we went from Borland and EDG reporting the original warning to EDG and Digital Mars now reporting a different warning. No win.

    So the original method I presented is still the only portable way that’s been suggested so far, and it’s basically identical to Boost’s (Boost’s just spells out the mostly-redundant “inline” and “const”; see earlier comment above).

  16. … Although in original example (Constraints), it won’t do anything.
    But still, would be good if this will be listed here.

  17. I think just simple

    #define UNUSED(x)

    will do the job.
    It can be used for function formal parameters as well.

  18. @Maxim: Hi Maxim! See my note to James; alas, those options don’t actually portably cause compilers to be quiet. Writing static_cast<void>(x) has the same effect as (void)x on the compilers I tried; it causes only one compiler to stop complaining.

    @Dmitry: The const isn’t needed, my version works correctly already on the code you gave. See below for details.

    @Dmitry, @John: Yup, In general I might like the name “unused” better than “ignore”. And Boost’s name contains both words. :-)

    @All: Yes, Boost’s is identical to the one I posted except that it does two things that aren’t really needed:

    1. Boost’s explicitly says “inline.” This is usually redundant in practice, though I agree that saying it explicitly doesn’t hurt. The reason it’s redundant in practice is: (a) popular compilers aggressively inline functions anyway even if you didn’t mark them inline, as long as their bodies are visible at compile time (and sometimes even if they aren’t, see for example VC++ /LTCG); and (b) this is a template, and all portable templates use the inclusion model and therefore are included with their full bodies, so they are effectively implicitly inline because of (a).

    2. Boost’s takes the parameter by const&. That isn’t necessary because template argument deduction will figure out the const anyway. I tried my version with const objects and it worked fine on all compilers, including very old ones.

    For bonus points: What about volatile?

    Any time you see “const”, also think “volatile” because it’s treated the same way in the type system. Incidentally, that’s another realistic case where the program performs writes that appear to be dead but aren’t because they go outside the program, namely to hardware.

    Q: Does my version and/or Boost’s work with volatile objects? A: Yes, both also work fine with volatile objects, because template argument deduction also figures out the volatile for you anyway. If you want to be super explicit, like Boost was with const&, you’d actually want to take the parameter by const volatile &. But now I’m just being picky to point out that writing either qualifier is redundant, and they didn’t need to write the const either.

    Nitpicker’s corner: Yes, I know that compilers are much less likely to warn about apparently-unused volatile variables because they know those variables are for hardware communication and writes to them are never dead. But in fact one compiler I’m testing with does still emit the warning even on apparently-unused volatile variables (it’s the Y2K-vintage Borland 5.5.1 again).

  19. boost::ignore_unused_variable_warning(x); is verbose but nice. <3 boost.

  20. Maxim,
    IMHO
    unused(p0, p3, p7);
    looks nicer than:
    static_cast(p0);
    static_cast(p3);
    static_cast(p7);
    And it’s a bit easier to search by ‘unused’ than by ‘static_cast’.

  21. The same effect can be achieved without having to define a function template.

    static_cast(x);

    Or, as James suggested, using C-style cast:

    (void)x;

  22. Why not:
    template void ignore(T const&) {}
    ?

    This will work in:
    void foo(…, int const bar, …)
    {
    ignore(bar);
    }
    and in:
    ignore(baz()); // some compilers may complain on unused return value
    too.

    Humm… and what about:
    template void ignore(T1 const&) {}
    template void ignore(T1 const&, T2 const&) {}
    template void ignore(T1 const&, T2 const&, T3 const&) {}

    Sometimes several function parameters are unused, and such form is a kind of more compact.

  23. @James: I tried your (void)pb suggestion on the compilers I tested against, and it was only partly effective. It silenced the warning on one compiler (year-2000-vintage Borland 5.5.1), but did not silence the warning on EDG-based compilers in particular (EDG is the compiler front end that’s used in compilers by Intel, Borland, Comeau, and others).

  24. What about:

    (void) pb;

    I think that’s pretty idiomatic (at least in C), and I think should work fairly portably (conjecture on my part).

Comments are closed.