21

In C++ sometimes a variable will be defined, but not used. Here's an example - a function for use with COM_INTERFACE_ENTRY_FUNC_BLIND ATL macro:

HRESULT WINAPI blindQuery( void* /*currentObject*/, REFIID iid, void** ppv, DWORD_PTR /*param*/ ) 
{
    DEBUG_LOG( __FUNCTION__ ); //DEBUG_LOG macro expands to an empty string in non-debug
    DEBUG_LOG( iid );
    iid; // <<<<<<<----silence compiler warning
    if( ppv == 0 ) {
        return E_POINTER;
    }
    *ppv = 0;
    return E_NOINTERFACE;
}

In the above example iid parameter is used with DEBUG_LOG macro that expands into an empty string in non-debug configurations. So commenting out or removing the iid variable name in the signature is not an option. When non-debug configurations are being compiled the compiler spawns a C4100: 'iid' : unreferenced formal parameter warning, so in order to silence the warning the iid; statement that is believed to be a no-op is added.

The question is the following: if we have any of the following declarations:

 CSomeType variableName; //or
 CSomeType& variableName; //or
 CSomeType* variableName;

will the following statement in C++ code:

variableName;

be a no-op at all times independent of what CSomeType is?

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • 2
    Won't this trigger a "statement has no effect" warning? – falstro Oct 27 '10 at 07:47
  • @roe: It doesn't on Visual C++. Maybe it does on some other compilers. – sharptooth Oct 27 '10 at 07:50
  • If between the declaration and the statement you abuse the preprocessor it could have an effect. For example `#define variableName exit(-1)` – Benoit Oct 27 '10 at 07:50
  • Also you cant declare reference without initialization. So you will get "error C2530: 'variableName' : references must be initialized" error. However, you can use UNREFERENCED_PARAMETER macro which is defined in winnt.h. – gtikok Oct 27 '10 at 07:53
  • @Benoit: it's not a variable name anymore at that point, duh. – MSalters Oct 27 '10 at 08:15
  • Looking at the options you could just wrap the declaration in a macro as well. This takes care of the problem "..., REFIID ONLY_DEBUG(iid), ..." Then in debug mode you can omit the name of the parameter. – edA-qa mort-ora-y Oct 29 '10 at 06:51
  • yes, gcc would produce a 'statement has no effect' warning – justin Feb 01 '11 at 10:39

2 Answers2

52

Yes, but you'll likely get another warning.

The standard way of doing this is: (void)iid;.


Very technically, this could still load iid into a register and do nothing. Granted that's extremely stupid on the compilers part (I doubt any would ever do that, if it does delete the compiler), but it's a more serious issue if the expression to be ignored is something concerning observable behavior, like calls to IO functions or the reading and writing of volatile variables.

This brings up an interesting question: Can we take an expression and completely ignore it?

That is, what we have now is this:

#define USE(x) (void)(x)

// use iid in an expression to get rid of warning, but have no observable effect
USE(iid); 

// hm, result of expression is gone but expression is still evaluated
USE(std::cout << "hmmm" << std::endl);

This is close to a solution:

// sizeof doesn't evaluate the expression
#define USE(x) (void)(sizeof(x))

But fails with:

void foo();

// oops, cannot take sizeof void
USE(foo());

The solution is to simply:

// use expression as sub-expression,
// then make type of full expression int, discard result
#define USE(x) (void)(sizeof((x), 0))

Which guarantees no operation.

Edit: The above indeed guaranteed no effect, but I posted without testing. Upon testing, it generates a warning again, at least in MSVC 2010, because the value isn't used. That's no good, time for more tricks!


Reminder: We want to "use" an expression without evaluating it. How can this be done? Like this:

#define USE(x) ((void)(true ? 0 : (x)))

This has a simple problem like last time (worse actually), in that (x) needs to be be convertible to int. This is, again, trivial to fix:

#define USE(x) ((void)(true ? 0 : ((x), 0)))

And we're back to same kind of effect we had last time (none), but this time x is "used" so we don't get any warnings. Done, right?

There is actually still one problem with this solution (and was present in the last un-solution as well, but went unnoticed), and it comes up in this example:

struct foo {};
void operator,(const foo&, int) {}

foo f;
USE(f); // oops, void isn't convertible to int!

That is, if the type of the expression (x) overloads the comma operator to something not convertible to int, the solution fails. Sure, unlikely, but for the sake of going completely overboard, we can fix it with:

#define USE(x) ((void)(true ? 0 : ((x), void(), 0)))

To make sure we really end up with zero. This trick brought to you by Johannes.


Also as noted, if the above wasn't enough, a stupid enough compiler could potentially "load" the expression 0 (into a register or something), then disregard it.

I think it's impossible to be rid of that, since we ultimately need an expression to result in a type of some sort to ignore, but if I ever think of it I'll add it.

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Though I agree, this doesn't mean the expression has to become a no-op. The compiler could, if it so desired, load the value into a register and then ignore it. While this would be silly it isn't unfathomable that an optimizing compiler leaves bits around sometimes. – edA-qa mort-ora-y Oct 27 '10 at 11:06
  • @eda: It is required to do nothing. Please see the quote [here](http://stackoverflow.com/questions/4031228/why-is-operator-void-not-invoked-with-cast-syntax/4031487#4031487). – GManNickG Oct 27 '10 at 18:13
  • 1
    No, the expression "value" is discarded. Loading the value into a register, or otherwise indicating its existence to the CPU would not violate that rule. As far the language is concerned it had no discernable side-effects. – edA-qa mort-ora-y Oct 28 '10 at 08:20
  • @edA: but it would be a waste of time to do so, and the compiler wouldn't merely have to avoid optimizing, it'd have to actively de-optimize the code. – jalf Oct 28 '10 at 10:05
  • 1
    I'm not saying a good optimizer would do this, nor do I know a case where it does, but I'm saying the author of the optimizer may have made a mistake -- it happens. So if it is absolutely critical that no code is generated, then it should not be present at all. – edA-qa mort-ora-y Oct 28 '10 at 11:47
  • @edA: What of the last `USE` definition? – GManNickG Oct 28 '10 at 19:21
  • Again, I don't see anything in the standard that would prevent 0 from being evaluated. But more importantly the (x) will be evaluated in this case, not what we want. To solve the original problem we should probably put the original parameter in a macro and attach the "unused" parameter (this works for GCC, other compilers must have an alternative)... – edA-qa mort-ora-y Oct 29 '10 at 06:50
  • @edA: Huh? The whole thing is inside of `sizeof`, which does not evaluate its arguments. Granted, I guess it could conceivably load the constant result into a register and ignore it again. – GManNickG Oct 29 '10 at 09:32
  • You are correct. sizeof should not evaluate the expression -- it resolves only into a constant time variable. – edA-qa mort-ora-y Oct 29 '10 at 11:35
  • I get the warning "left operand of comma has no effect" from GCC. Any ideas? – Steven Lu Jun 02 '11 at 17:19
  • @Steven: Hm, nope. I wouldn't really try to use the macro, it was more of a how-far-is-too-far answer. – GManNickG Jun 02 '11 at 18:09
  • Okay. I'm just using this: `#define UNUSED(x) ((void)(sizeof(x)))` – Steven Lu Jun 03 '11 at 13:32
  • @Steven Lu: Okay, but I think that warns on MSVC. It's better to not have the variable in the first place. – GManNickG Jun 03 '11 at 20:38
  • @GMan I might use a conditional to check for MSVC then. I like to stick with gcc anyway. Thanks. – Steven Lu Jun 06 '11 at 13:54
  • 1
    This solution gives a warning in g++ 4.9.2. – Zingam Jan 17 '15 at 02:05
  • The solution from 2017 gives `warning: left operand of comma operator has no effect [-Wunused-value]` on GCC 5.4.0, 5.5.0, and 9.2.1, `warning: expression result unused [-Wunused-value]` on XCode 11.3.1 and worked perfectly on MSVC 19.16.27038.0 – aPonza Apr 17 '20 at 10:13
  • @jalf I presume you've figured this out in the decade since that comment, but it does not require active de-optimization: the most simple compiler implementation just recurses down through expressions to the smallest expression it can generate machine code for, and generates the simplest machine code which combines to execute the specified behavior correctly (this is how you get GCC at no optimization spitting out code which just moves stuff to and from the stack repeatedly just to do a couple additions). In such a compiler, no active de-optimization is needed for `(void)x` to compile as `x`. – mtraceur May 13 '22 at 15:45
2

Well, it's not really possible to say with 100% certainty without looking at the compiler's source code, but I would be very surprised if this ever generated any code in modern compilers.

At the end of the day, if you're worried about any particular instance then you are always free to look at the generated assembly code.

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168