Variable assignment in “if” condition [duplicate]

北城以北 提交于 2019-12-28 02:06:05

问题


I recently just lost some time figuring out a bug in my code which was caused by a typo:

if(a=b)

instead of:

if(a==b)

I was wondering if there is any particular case you would want to assign a value to a variable in a if statement, or if not, why doesn't the compiler throw a warning or an error ?


回答1:


if (Derived* derived = dynamic_cast<Derived*>(base)) {
   // do stuff with `derived`
}

Though this is oft cited as an anti-pattern ("use virtual dispatch!"), sometimes the Derived type has functionality that the Base simply does not (and, consequently, distinct functions), and this is a good way to switch on that semantic difference.




回答2:


Here is some history on the syntax in question.

In classical C, error handling was frequently done by writing something like:

int error;
...
if(error = foo()) {
    printf("An error occured: %s\nBailing out.\n", strerror(error));
    abort();
}

Or, whenever there was a function call that might return a null pointer, the idiom was used the other way round:

Bar* myBar;
... //in old C variables had to be declared at the start of the scope
if(myBar = getBar()) {
    //do something with myBar
}

However, this syntax is dangerously close to

if(myValue == bar()) ...

which is why many people consider the assignment inside a condition bad style, and compilers started to warn about it (at least with -Wall). However, this warning can be avoided by adding an extra set of parentheses:

if((myBar = getBar())) {  //tells the compiler: Yes, I really want to do that assignment!

Then C99 came around, allowing you to mix definitions and statements, so many developers would frequently write something like

Bar* myBar = getBar();
if(myBar) {

which does feel awkward. This is why the newest standard allows definitions inside conditions, to provide a short, elegant way to do this:

if(Bar* myBar = getBar()) {

There is no danger in this statement anymore, you explicitely give the variable a type, obviously wanting it to be initialized. It also avoids the extra line to define the variable, which is nice. But most importantly, the compiler can now easily catch this sort of bug:

if(Bar* myBar = getBar()) {
    ...
}
foo(myBar->baz);  //compiler error
//or, for the C++ enthusiasts:
myBar->foo();     //compiler error

Without the variable definition inside the if statement, this condition would not be detectable.

To make a long answer short: The syntax in you question is the product of old C's simplicity and power, but it is evil, so compilers can warn about it. Since it is also a very useful way to express a common problem, there is now a very concise, bug robust way to achieve the same behaviour. And there is a lot of good, possible uses for it.




回答3:


The assignment operator returns the value of the assigned value. So, I might use it in situation like this:

if(x = getMyNumber())

Then I assign x to be the value returned by getMyNumber and I check if it's not zero.

Avoid doing that, I gave you an example just to help you understand this.

Edit: adding Just a suggestion (one may like).

To avoid such bugs up-to some extends one should write if condition as if(NULL == ptr) instead of if(ptr == NULL) Because When you misspell the equality check operator == as operator =, the compile will throw a lvalue error with if(NULL = ptr), but if(res = NULL) passed by compiler(which is not what you mean) and remain a bug in code for runtime.
For the same reason I will prefer to write if(getMyNumber() ==x) instead of if(x == getMyNumber())

One should also read: Criticism regarding this kind of code.




回答4:


It depends on whether you want to write clean code or not. When C was first being developed, the importance of clean code wasn't fully recognized, and compilers were very simplistic: using nested assignment like this could often result in faster code. Today, I can't think of any case where a good programmer would do it. It just makes the code less readable and more difficult to maintain.




回答5:


I ran into a case where this was useful just recently so I thought I'd post it.

Suppose you want to check several conditions in a single if, and if any one of the conditions is true, you'd like to generate an error message. If you want to include in your error message which specific condition caused the error, you could do the following:

std::string e;
if( myMap[e = "ab"].isNotValid() ||
    myMap[e = "cd"].isNotValid() ||
    myMap[e = "ef"].isNotValid() )
{
    // here, e has the key for which the validation failed
}

So if the second condition is the one that evaluates to true, e will be equal to "cd". This is due to the short-circuit behaviour of || which is mandated by the standard (unless overloaded). See this answer for more details on short-circuiting.




回答6:


why doesn't the compiler throw a warning

Some compilers will generate warnings for suspicious assignments in a conditional expression, though you usually have to enable the warning explicitly.

For example, in Visual C++, you have to enable C4706 (or level 4 warnings in general). I generally turn on as many warnings as I can and make the code more explicit in order to avoid false positives. For example, if I really wanted to do this:

if (x = Foo()) { ... }

Then I'd write it as:

if ((x = Foo()) != 0) { ... }

The compiler sees the explicit test and assumes that the assignment was intentional, so you don't get a false positive warning here.

The only drawback with this approach is that you can't use it when the variable is declared in the condition. That is, you cannot rewrite:

if (int x = Foo()) { ... }

as

if ((int x = Foo()) != 0) { ... }

Syntactically, that doesn't work. So you either have to disable the warning, or compromise on how tightly you scope x.

UPDATE: C++17 added the ability to have an init-statement in the condition for an if-statement (p0305r1), which solves this problem nicely (for kind of comparison, not just != 0).

if (x = Foo(); x != 0) { ... }

Furthermore, if you want, you can limit the scope of x to just the if-statement:

if (int x = Foo(); x != 0) { /* x in scope */ ... }
// x out of scope



回答7:


Doing assignment in an if is a fairly common thing, though it's also common that people do it by accident.

The usual pattern is:

if (int x = expensive_function_call())
{
  // ...do things with x
}

The anti-pattern is where you're mistakenly assigning to things:

if (x = 1)
{
  // Always true
}
else
{
  // Never happens
}

You can avoid this to a degree by putting your constants or const values first, so your compiler will throw an error:

if (1 = x)
{
  // Compiler error, can't assign to 1
}

= vs. == is something you'll need to develop an eye for. I usually put whitespace around the operator so it's more obvious which operation is being performed, as longname=longername looks a lot like longname==longername at a glance, but = and == on their own are obviously different.



来源:https://stackoverflow.com/questions/17681535/variable-assignment-in-if-condition

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!