Will a "variableName;" C++ statement be a no-op at all times?

Solution 1:

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.

Solution 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.