Will a "Variablename;" C++ Statement Be a No-Op at All Times

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

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.

What's a portable way to implement no-op statement in C++?

I suspect that it might trigger warnings on some compilers

Unlikely, since ((void)0) is what the standard assert macro expands to when NDEBUG is defined. So any compiler that issues warnings for it will issue warnings whenever code that contains asserts is compiled for release. I expect that would be considered a bug by the users.

I suppose a compiler could avoid that problem by warning for your proposal (void)0 while treating only ((void)0) specially. So you might be better off using ((void)0), but I doubt it.

In general, casting something to void, with or without the extra enclosing parens, idiomatically means "ignore this". For example in C code that casts function parameters to void in order to suppress warnings for unused variables. So on that score too, a compiler that warned would be rather unpopular, since suppressing one warning would just give you another one.

Note that in C++, standard headers are permitted to include each other. Therefore, if you are using any standard header, assert might have been defined by that. So your code is non-portable on that account. If you're talking "universally portable", you normally should treat any macro defined in any standard header as a reserved identifier. You could undefine it, but using a different name for your own assertions would be more sensible. I know it's only an example, but I don't see why you'd ever want to define assert in a "universally portable" way, since all C++ implementations already have it, and it doesn't do what you're defining it to do here.

What is the use of a statement with no effect in C++?

This construct is a common way of tricking the compiler into not emitting a warning for unused parameters. I have not seen it used for any other purpose.

(void)input;

While it is common, it is also a really bad idea.

  • it is highly platform dependent -- it may work on one compiler and not another.
  • it is unnecessary. There is always a better way to deal with unused parameters. The modern way is to simply omit the parameter name.
  • it can get left behind if the code changes and the parameter is now used (as appears to be the case here).
  • it can backfire. Some compilers may treat this as invalid.

According to the C++ standard N3936 S5.4/11:

In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression. The expression is evaluated and its value is discarded.

A compiler would be entitled to observe that there is no side-effect and therefore this construct deserves at least a warning. According to @chris, MSVC is one of those compilers.

How to make sure that a variable is not used

C++ allows you to have unnamed function parameters. It's exactly useful for situations where you do not intend on using the corresponding parameter, yet must declare it to match the signature e.g. when writing a callback to some library.

Simply redefine your function:

void f(int&) {
// code ...
}

What's the reason for this no-op while-loop used for assert macro?

Most likely to avoid compiler warnings. Check whether this code provokes a warning about an empty statement:

if (foo);

If it does, then do you want the same warning in release mode for the following code?

if (foo) assert(what);

C99 (which is relevant to C++11) also says that assert expands "to a void expression". IIRC, whitespace alone isn't an expression, even though whitespace followed by a semi-colon is an expression-statement. Good old BNF grammar.

By the way, this definition of assert is not standard-conforming. C89 and C99 both say that when NDEBUG is defined, then assert is defined as:

#define assert(ignore) ((void)0)

I'm not sure whether the authors consider it an important requirement, but a program could for example stringify the macro expansion and expect a particular result.

Universally compiler independent way of implementing an UNUSED macro in C/C++

According to this answer by user GMan the typical way is to cast to void:

#define UNUSED(x) (void)(x)

but if x is marked as volatile that would enforce reading from the variable and thus have a side effect and so the actual way to almost guarantee a no-op and suppress the compiler warning is the following:

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

switch-case statement without break

The break acts like a goto command. Or, as a better example, it is like when using return in a void function. Since it is at the end, it makes no difference whether it is there or not. Although, I do like to include it.

Why is (void) 0 a no operation in C and C++?

(void)0 (+;) is a valid, but 'does-nothing' C++ expression, that's everything. It doesn't translate to the no-op instruction of the target architecture, it's just an empty statement as placeholder whenever the language expects a complete statement (for example as target for a jump label, or in the body of an if clause).

From Chris Lutz's comment:

It should be noted that, when used as a macro (say, #define noop ((void)0)), the (void) prevents it from being accidentally used as a value (like in int x = noop;).

For the above expression the compiler will rightly flag it as an invalid operation. GCC spits error: void value not ignored as it ought to be and VC++ barks 'void' illegal with all types.



Related Topics



Leave a reply



Submit