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?
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.
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 occurred: %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). Some compilers allow avoiding this warning by adding an extra set of parentheses:
if((myBar = getBar())) { // It tells the compiler: Yes, I really want to do that assignment!
However, this is ugly and somewhat of a hack, so it's better avoid writing such code.
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 isn't any danger in this statement anymore. You explicitly 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
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 behavior. And there are a lot of good, possible uses for it.
The assignment operator returns the value of the assigned value. So, I might use it in a situation like this:
if (x = getMyNumber())
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.
To avoid such bugs up to some extent, one should write the if condition as if(NULL == ptr) instead of if (ptr == NULL). Because when you misspell the equality check operator == as operator =, the compiler will throw an lvalue error with if (NULL = ptr), but if (res = NULL) passed by the compiler (which is not what you mean) and remain a bug in code for runtime.
One should also read Criticism regarding this kind of code.
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.
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
In C++17, one can use:
if (<initialize> ; <conditional_expression>) { <body> }
Similar to a for loop iterator initializer.
Here is an example:
if (Employee employee = GetEmployee(); employee.salary > 100) { ... }
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.
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.
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.
a quite common case.
use
if (0 == exit_value)
instead of
if (exit_value == 0)
this kind of typo will cause compile error
Related
I read that in C++17 we can initialize variables in if statements like this
if (int length = 2; length == 2)
//execute something
Instead of
int length = 2;
if (length == 2)
//do something
Even though it is shorter, it affects the code readability (especially for people who don't know this new feature), which I suppose is a bad coding practice for large software development.
Is there any advantage of using this feature other than making the code shorter?
It limits the scope of length to the if alone. So you get the same benefits we originally got when we were allowed to write
for(int i = 0; i < ... ; ++i) {
// ...
}
Instead of the variable leaking
int i;
for(i = 0; i < ... ; ++i) {
// ...
}
Short lived variables are better for several reasons. But to name a couple:
The shorter something lives, the less things you need to keep in mind when reading unrelated lines of code. If i doesn't exist outside the loop or if statement, then we don't need to mind its value outside of them. Nor do we need to worry its value will interact with other parts of the program that are outside of its intended scope (which may happen if i above is reused in another loop). It makes code easier to follow and reason about.
If the variable holds a resource, then that resource is now held for the shortest period possible. And this is without extraneous curly braces. It's also made clear the resource is related to the if alone. Consider this as a motivating example
if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
}
If your colleagues aren't aware of the feature, teach them! Appeasing programmers who don't wish to learn is a poor excuse to avoid features.
Is there any advantage of using this feature other than making the code shorter?
You reduce variable scope. This does make sense and increases readability, as it strengthens the locality of identifiers you need to reason about. I agree that long init statements inside if statements should be avoided, but for short stuff, it's fine.
Note that you can already do initialization and branching on the result in pre-C++17:
int *get(); // returns nullptr under some condition
if (int *ptr = get())
doStuff();
This is subject to one's personal opinion, but you can consider an explicit condition more readable:
if (int *ptr = get(); ptr != nullptr)
doStuff();
Besides, arguing against the readability of a feature by referring to the fact that people aren't used to it is dangerous. People weren't used to smart pointers at some point, yet still we all agree today (I guess) that it's a good thing they are there.
The new form of the if statement has many uses.
Currently, the initializer is either declared before the statement and
leaked into the ambient scope, or an explicit scope is used. With the
new form, such code can be written more compactly, and the improved
scope control makes some erstwhile error-prone constructions a bit
more robust.
Open Standard Proposal for If statement with initializer
So, in summary, this statement simplifies common code patterns and helps users keep scopes tight.
I hope it helps!
In the interest of minimizing the scope of variables there is an idiom which defines a resource only if it is valid upon creation (for example file stream objects):
if(auto file = std::ifstream("filename"))
{
// use file here
}
else
{
// complain about errors here
}
// The identifier `file` does not pollute the wider scope
Sometimes you want to be able to reverse the logic of that test to make the failure the primary clause and the valid resource the else clause. This was previously not possible. But now we can do:
if(auto file = std::ifstream("filename"); !file)
{
// complain about errors here
}
else
{
// use file here
}
An example might be throwing an exception:
if(auto file = std::ifstream(filename); !file)
throw std::runtime_error(std::strerror(errno));
else
{
// use file here
}
Some people like to code so that a function will abort early on an error and otherwise progress. This idiom puts the abort logic physically above the continuation logic which some people may find more natural.
It is especially useful for logical events. Consider this example:
char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
std::cerr << "bad stuff\n";
}
Seems a bit rough. Unless you are very familiar with OR, AND with negations, you might need to pause and think about this logic - which is generally poor design. With the if-initialization you can add expressiveness.
char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
std::cerr << "bad stuff\n";
}
the named variable can be re-used inside the if too. E.g:
if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
std::cerr << distance << " is too small\n";
}
This is great, especially given that the variable is scoped and therefore doesn't pollute the space afterwards.
This is an extension of an existing feature, which aids readability in my experience.
if (auto* ptr = get_something()) {
}
Here we both create the variable ptr and we test against it being non-null. The scope of ptr is limited to where it is valid. It is far, far easier to convince yourself that all use of ptr is valid.
But what if we are talking about something that doesn't convert to bool that way?
if (auto itr = find(bob)) {
}
That doesn't work. But with this new feature we can:
if (auto itr = find(bob); itr != end()) {
}
Add a clause saying "when is this initialization valid".
In essence, this gives us a set of tokens that mean "initialize some expression, and when it is valid, do some code. When it is not valid, discard it."
It has been idiomatic to do the pointer-test trick since C++98. Once you have embraced that, this extension is natural.
I read that in C++17 we can initialize variables in if statements like this
if (int length = 2; length == 2)
//execute something
Instead of
int length = 2;
if (length == 2)
//do something
Even though it is shorter, it affects the code readability (especially for people who don't know this new feature), which I suppose is a bad coding practice for large software development.
Is there any advantage of using this feature other than making the code shorter?
It limits the scope of length to the if alone. So you get the same benefits we originally got when we were allowed to write
for(int i = 0; i < ... ; ++i) {
// ...
}
Instead of the variable leaking
int i;
for(i = 0; i < ... ; ++i) {
// ...
}
Short lived variables are better for several reasons. But to name a couple:
The shorter something lives, the less things you need to keep in mind when reading unrelated lines of code. If i doesn't exist outside the loop or if statement, then we don't need to mind its value outside of them. Nor do we need to worry its value will interact with other parts of the program that are outside of its intended scope (which may happen if i above is reused in another loop). It makes code easier to follow and reason about.
If the variable holds a resource, then that resource is now held for the shortest period possible. And this is without extraneous curly braces. It's also made clear the resource is related to the if alone. Consider this as a motivating example
if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
}
If your colleagues aren't aware of the feature, teach them! Appeasing programmers who don't wish to learn is a poor excuse to avoid features.
Is there any advantage of using this feature other than making the code shorter?
You reduce variable scope. This does make sense and increases readability, as it strengthens the locality of identifiers you need to reason about. I agree that long init statements inside if statements should be avoided, but for short stuff, it's fine.
Note that you can already do initialization and branching on the result in pre-C++17:
int *get(); // returns nullptr under some condition
if (int *ptr = get())
doStuff();
This is subject to one's personal opinion, but you can consider an explicit condition more readable:
if (int *ptr = get(); ptr != nullptr)
doStuff();
Besides, arguing against the readability of a feature by referring to the fact that people aren't used to it is dangerous. People weren't used to smart pointers at some point, yet still we all agree today (I guess) that it's a good thing they are there.
The new form of the if statement has many uses.
Currently, the initializer is either declared before the statement and
leaked into the ambient scope, or an explicit scope is used. With the
new form, such code can be written more compactly, and the improved
scope control makes some erstwhile error-prone constructions a bit
more robust.
Open Standard Proposal for If statement with initializer
So, in summary, this statement simplifies common code patterns and helps users keep scopes tight.
I hope it helps!
In the interest of minimizing the scope of variables there is an idiom which defines a resource only if it is valid upon creation (for example file stream objects):
if(auto file = std::ifstream("filename"))
{
// use file here
}
else
{
// complain about errors here
}
// The identifier `file` does not pollute the wider scope
Sometimes you want to be able to reverse the logic of that test to make the failure the primary clause and the valid resource the else clause. This was previously not possible. But now we can do:
if(auto file = std::ifstream("filename"); !file)
{
// complain about errors here
}
else
{
// use file here
}
An example might be throwing an exception:
if(auto file = std::ifstream(filename); !file)
throw std::runtime_error(std::strerror(errno));
else
{
// use file here
}
Some people like to code so that a function will abort early on an error and otherwise progress. This idiom puts the abort logic physically above the continuation logic which some people may find more natural.
It is especially useful for logical events. Consider this example:
char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
std::cerr << "bad stuff\n";
}
Seems a bit rough. Unless you are very familiar with OR, AND with negations, you might need to pause and think about this logic - which is generally poor design. With the if-initialization you can add expressiveness.
char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
std::cerr << "bad stuff\n";
}
the named variable can be re-used inside the if too. E.g:
if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
std::cerr << distance << " is too small\n";
}
This is great, especially given that the variable is scoped and therefore doesn't pollute the space afterwards.
This is an extension of an existing feature, which aids readability in my experience.
if (auto* ptr = get_something()) {
}
Here we both create the variable ptr and we test against it being non-null. The scope of ptr is limited to where it is valid. It is far, far easier to convince yourself that all use of ptr is valid.
But what if we are talking about something that doesn't convert to bool that way?
if (auto itr = find(bob)) {
}
That doesn't work. But with this new feature we can:
if (auto itr = find(bob); itr != end()) {
}
Add a clause saying "when is this initialization valid".
In essence, this gives us a set of tokens that mean "initialize some expression, and when it is valid, do some code. When it is not valid, discard it."
It has been idiomatic to do the pointer-test trick since C++98. Once you have embraced that, this extension is natural.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Inadvertent use of = instead of ==
C++ compilers let you know via warnings that you wrote,
if( a = b ) { //...
And that it might be a mistake that you certainly wanted to write:
if( a == b ) { //...
But is there a case where the warning should be ignored, because it's a good way to use this "feature"?
I don't see any code clarity reason possible, so is there a case where it’s useful?
Two possible reasons:
Assign & Check
The = operator (when not overriden) normally returns the value that it assigned. This is to allow statements such as a=b=c=3. In the context of your question, it also allows you to do something like this:
bool global;//a global variable
//a function
int foo(bool x){
//assign the value of x to global
//if x is equal to true, return 4
if (global=x)
return 4;
//otherwise return 3
return 3;
}
...which is equivalent to but shorter than:
bool global;//a global variable
//a function
int foo(bool x){
//assign the value of x to global
global=x;
//if x is equal to true, return 4
if (global==true)
return 4;
//otherwise return 3
return 3;
}
Also, it should be noted (as stated by Billy ONeal in a comment below) that this can also work when the left-hand argument of the = operator is actually a class with a conversion operator specified for a type which can be coerced (implicitly converted) to a bool. In other words, (a=b) will evaulate to true or false if a is of a type which can be coerced to a boolean value.
So the following is a similar situation to the above, except the left-hand argument to = is an object and not a bool:
#include <iostream>
using namespace std;
class Foo {
public:
operator bool (){ return true; }
Foo(){}
};
int main(){
Foo a;
Foo b;
if (a=b)
cout<<"true";
else
cout<<"false";
}
//output: true
Note: At the time of this writing, the code formatting above is bugged. My code (check the source) actually features proper indenting, shift operators and line spacing. The <'s are supposed to be <'s, and there aren't supposed to be enourmous gaps between each line.
Overridden = operator
Since C++ allows the overriding of operators, sometimes = will be overriden to do something other than what it does with primitive types. In these cases, the performing the = operation on an object could return a boolean (if that's how the = operator was overridden for that object type).
So the following code would perform the = operation on a with b as an argument. Then it would conditionally execute some code depending on the return value of that operation:
if (a=b){
//execute some code
}
Here, a would have to be an object and b would be of the correct type as defined by the overriding of the = operator for objects of a's type. To learn more about operator overriding, see this wikipedia article which includes C++ examples: Wikipedia article on operator overriding
while ( (line = readNextLine()) != EOF) {
processLine();
}
You could use to test if a function returned any error:
if (error_no = some_function(...)) {
// Handle error
}
Assuming that some_function returns the error code in case of an error. Or zero otherwise.
This is a consequence of basic feature of the C language:
The value of an assignment operation is the assigned value itself.
The fact that you can use that "return value" as the condition of an if() statement is incidental.
By the way, this is the same trick that allows this crazy conciseness:
void strcpy(char *s, char *t)
{
while( *s++ = *t++ );
}
Of course, the while exits when the nullchar in t is reached, but at the same time it is copied to the destination s string.
Whether it is a good idea, usually not, as it reduce code readability and is prone to errors.
Although the construct is perfectly legal syntax and your intent may truly be as shown below, don't leave the "!= 0" part out.
if( (a = b) != 0 ) {
...
}
The person looking at the code 6 months, 1 year, 5 years from now, at first glance, is simply going to believe the code contains a "classic bug" written by a junior programmer and will try to "fix" it. The construct above clearly indicates your intent and will be optimized out by the compiler. This would be especially embarrassing if you are that person.
Your other option is to heavily load it with comments. But the above is self-documenting code, which is better.
Lastly, my preference is to do this:
a = b;
if( a != 0 ) {
...
}
This is about a clear as the code can get. If there is a performance hit, it is virtually zero.
A common example where it is useful might be:
do {
...
} while (current = current->next);
I know that with this syntax you can avoid putting an extra line in your code, but I think it takes away some readability from the code.
This syntax is very useful for things like the one suggested by Steven Schlansker, but using it directly as a condition isn't a good idea.
This isn't actually a deliberate feature of C, but a consequence of two other features:
Assignment returns the assigned value
This is useful for performing multiple assignments, like a = b = 0, or loops like while ((n = getchar()) != EOF).
Numbers and pointers have truth values
C originally didn't have a bool type until the 1999 standard, so it used int to represent Boolean values. Backwards compatibility requires C and C++ to allow non-bool expressions in if, while, and for.
So, if a = b has a value and if is lenient about what values it accepts, then if (a = b) works. But I'd recommend using if ((a = b) != 0) instead to discourage anyone from "fixing" it.
You should explicitly write the checking statement in a better coding manner, avoiding the assign & check approach. Example:
if ((fp = fopen("filename.txt", "wt")) != NULL) {
// Do something with fp
}
void some( int b ) {
int a = 0;
if( a = b ) {
// or do something with a
// knowing that is not 0
}
// b remains the same
}
But is there a case where the warning
should be ignored because it's a good
way to use this "feature"? I don't see
any code clarity reason possible so is
there a case where its useful?
The warning can be suppressed by placing an extra parentheses around the assignment. That sort of clarifies the programmer's intent. Common cases I've seen that would match the (a = b) case directly would be something like:
if ( (a = expression_with_zero_for_failure) )
{
// do something with 'a' to avoid having to reevaluate
// 'expression_with_zero_for_failure' (might be a function call, e.g.)
}
else if ( (a = expression2_with_zero_for_failure) )
{
// do something with 'a' to avoid having to reevaluate
// 'expression2_with_zero_for_failure'
}
// etc.
As to whether writing this kind of code is useful enough to justify the common mistakes that beginners (and sometimes even professionals in their worst moments) encounter when using C++, it's difficult to say. It's a legacy inherited from C and Stroustrup and others contributing to the design of C++ might have gone a completely different, safer route had they not tried to make C++ backwards compatible with C as much as possible.
Personally I think it's not worth it. I work in a team and I've encountered this bug several times before. I would have been in favor of disallowing it (requiring parentheses or some other explicit syntax at least or else it's considered a build error) in exchange for lifting the burden of ever encountering these bugs.
while( (l = getline()) != EOF){
printf("%s\n", l);
}
This is of course the simplest example, and there are lots of times when this is useful. The primary thing to remember is that (a = true) returns true, just as (a = false) returns false.
Preamble
Note that this answer is about C++ (I started writing this answer before the tag "C" was added).
Still, after reading Jens Gustedt's comment, I realized it was not the first time I wrote this kind of answer. Truth is, this question is a duplicate of another, to which I gave the following answer:
Inadvertent use of = instead of ==
So, I'll shamelessly quote myself here to add an important information: if is not about comparison. It's about evaluation.
This difference is very important, because it means anything can be inside the parentheses of a if as long as it can be evaluated to a Boolean. And this is a good thing.
Now, limiting the language by forbidding =, where all other operators are authorized, is a dangerous exception for the language, an exception whose use would be far from certain, and whose drawbacks would be numerous indeed.
For those who are uneasy with the = typo, then there are solutions (see Alternatives below...).
About the valid uses of if(i = 0) [Quoted from myself]
The problem is that you're taking the problem upside down. The "if" notation is not about comparing two values like in some other languages.
The C/C++ if instruction waits for any expression that will evaluate to either a Boolean, or a null/non-null value. This expression can include two values comparison, and/or can be much more complex.
For example, you can have:
if(i >> 3)
{
std::cout << "i is less than 8" << std::endl
}
Which proves that, in C/C++, the if expression is not limited to == and =. Anything will do, as long as it can be evaluated as true or false (C++), or zero non-zero (C/C++).
About valid uses
Back to the non-quoted answer.
The following notation:
if(MyObject * p = findMyObject())
{
// uses p
}
enables the user to declare and then use p inside the if. It is a syntactic sugar... But an interesting one. For example, imagine the case of an XML DOM-like object whose type is unknown well until runtime, and you need to use RTTI:
void foo(Node * p_p)
{
if(BodyNode * p = dynamic_cast<BodyNode *>(p_p))
{
// this is a <body> node
}
else if(SpanNode * p = dynamic_cast<SpanNode *>(p_p))
{
// this is a <span> node
}
else if(DivNode * p = dynamic_cast<DivNode *>(p_p))
{
// this is a <div> node
}
// etc.
}
RTTI should not be abused, of course, but this is but one example of this syntactic sugar.
Another use would be to use what is called C++ variable injection. In Java, there is this cool keyword:
synchronized(p)
{
// Now, the Java code is synchronized using p as a mutex
}
In C++, you can do it, too. I don't have the exact code in mind (nor the exact Dr. Dobb's Journal's article where I discovered it), but this simple define should be enough for demonstration purposes:
#define synchronized(lock) \
if (auto_lock lock_##__LINE__(lock))
synchronized(p)
{
// Now, the C++ code is synchronized using p as a mutex
}
(Note that this macro is quite primitive, and should not be used as is in production code. The real macro uses a if and a for. See sources below for a more correct implementation).
This is the same way, mixing injection with if and for declaration, you can declare a primitive foreach macro (if you want an industrial-strength foreach, use Boost's).
About your typo problem
Your problem is a typo, and there are multiple ways to limit its frequency in your code. The most important one is to make sure the left-hand-side operand is constant.
For example, this code won't compile for multiple reasons:
if( NULL = b ) // won't compile because it is illegal
// to assign a value to r-values.
Or even better:
const T a ;
// etc.
if( a = b ) // Won't compile because it is illegal
// to modify a constant object
This is why in my code, const is one of the most used keyword you'll find. Unless I really want to modify a variable, it is declared const and thus, the compiler protects me from most errors, including the typo error that motivated you to write this question.
But is there a case where the warning should be ignored because it's a good way to use this "feature"? I don't see any code clarity reason possible so is there a case where its useful?
Conclusion
As shown in the examples above, there are multiple valid uses for the feature you used in your question.
My own code is a magnitude cleaner and clearer since I use the code injection enabled by this feature:
void foo()
{
// some code
LOCK(mutex)
{
// some code protected by a mutex
}
FOREACH(char c, MyVectorOfChar)
{
// using 'c'
}
}
... which makes the rare times I was confronted to this typo a negligible price to pay (and I can't remember the last time I wrote this type without being caught by the compiler).
Interesting sources
I finally found the articles I've had read on variable injection. Here we go!!!
FOR_EACH and LOCK (2003-11-01)
Exception Safety Analysis (2003-12-01)
Concurrent Access Control & C++ (2004-01-01)
Alternatives
If one fears being victim of the =/== typo, then perhaps using a macro could help:
#define EQUALS ==
#define ARE_EQUALS(lhs,rhs) (lhs == rhs)
int main(int argc, char* argv[])
{
int a = 25 ;
double b = 25 ;
if(a EQUALS b)
std::cout << "equals" << std::endl ;
else
std::cout << "NOT equals" << std::endl ;
if(ARE_EQUALS(a, b))
std::cout << "equals" << std::endl ;
else
std::cout << "NOT equals" << std::endl ;
return 0 ;
}
This way, one can protect oneself from the typo error, without needing a language limitation (that would cripple language), for a bug that happens rarely (i.e., almost never, as far as I remember it in my code).
There's an aspect of this that hasn't been mentioned: C doesn't prevent you from doing anything it doesn't have to. It doesn't prevent you from doing it because C's job is to give you enough rope to hang yourself by. To not think that it's smarter than you. And it's good at it.
Never!
The exceptions cited don't generate the compiler warning. In cases where the compiler generates the warning, it is never a good idea.
RegEx sample
RegEx r;
if(((r = new RegEx("\w*)).IsMatch()) {
// ... do something here
}
else if((r = new RegEx("\d*")).IsMatch()) {
// ... do something here
}
Assign a value test
int i = 0;
if((i = 1) == 1) {
// 1 is equal to i that was assigned to a int value 1
}
else {
// ?
}
My favourite is:
if (CComQIPtr<DerivedClassA> a = BaseClassPtr)
{
...
}
else if (CComQIPtr<DerivedClassB> b = BaseClassPtr)
{
...
}
In Stroustrup's The C++ Programming Language: Special Edition (3rd Ed), Stroustrup writes that the declaration and initialization of variables in the conditionals of control statements is not only allowed, but encouraged. He writes that he encourages it because it reduces the scope of the variables to only the scope that they are required for. So something like this...
if ((int i = read(socket)) < 0) {
// handle error
}
else if (i > 0) {
// handle input
}
else {
return true;
}
...is good programming style and practice. The variable i only exists for the block of if statements for which it is needed and then goes out of scope.
However, this feature of the programming language doesn't seem to be supported by g++ (version 4.3.3 Ubuntu specific compile), which is surprising to me. Perhaps I'm just calling g++ with a flag that turns it off (the flags I've called are -g and -Wall). My version of g++ returns the following compile error when compiling with those flags:
socket.cpp:130: error: expected primary-expression before ‘int’
socket.cpp:130: error: expected `)' before ‘int’
On further research I discovered that I didn't seem to be the only one with a compiler that doesn't support this. And there seemed to be some confusion in this question as to exactly what syntax was supposedly standard in the language and what compilers compile with it.
So the question is, what compilers support this feature and what flags need to be set for it to compile? Is it an issue of being in certain standards and not in others?
Also, just out of curiosity, do people generally agree with Stroustrup that this is good style? Or is this a situation where the creator of a language gets an idea in his head which is not necessarily supported by the language's community?
It is allowed to declare a variable in the control part of a nested block, but in the case of if and while, the variable must be initialized to a numeric or boolean value that will be interpreted as the condition. It cannot be included in a more complex expression!
In the particular case you show, it doesn't seem you can find a way to comply unfortunately.
I personally think it's good practice to keep the local variables as close as possible to their actual lifetime in the code, even if that sounds shocking when you switch from C to C++ or from Pascal to C++ - we were used to see all the variables at one place. With some habit, you find it more readable, and you don't have to look elsewhere to find the declaration. Moreover, you know that it is not used before that point.
Edit:
That being said, I don't find it a good practice to mix too much in a single statement, and I think it's a shared opinion. If you affect a value to a variable, then use it in another expression, the code will be more readable and less confusing by separating both parts.
So rather than using this:
int i;
if((i = read(socket)) < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
I would prefer that:
int i = read(socket);
if(i < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
I consider it a good style when used with possibly NULL pointer:
if(CObj* p = GetOptionalValue()) {
//Do something with p
}
This way whether p is declared, it is a valid pointer. No dangling pointer access danger.
On the other hand at least in VC++ it is the only use supported (i.e. checking whether assignment is true)
I use const as much as possible in these situations. Instead of your example, I would do:
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
So although the scope isn't contained, it doesn't really matter, since the variable can't be altered.
They are fixing this in c++17 :
if (int i = read(socket); i < 0)
where if can have an initializer statement.
see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.html
I've run into a similar problem:
The problem seems to be the parentheses around the int declaration. It should work if you can express the assignment and test without them, i.e.
if (int i = read(socket)) {
should work, but that means the test is != 0, which is not what you want.
While you can use a declaration as a boolean expression, you cannot place a declaration in the middle of an expression. I cannot help thinking that you are mis-reading what Bjarne is saying.
The technique is useful and desirable mostly for control variables of for loops, but in this instance I believe is ill-advised and does not serve clarity. And of course it does not work! ;)
if( <type> <identifier> = <initialiser> ) // valid, but not that useful IMO
if( (<type> <identifier> = <initialiser>) <operator> <operand> ) // not valid
for( <type> <identifier> = <initialiser>;
<expression>;
<expression> ) // valid and desirable
In your example you have called a function with side effects in a conditional, which IMO is a bad idea regardless of what you might think about declaring the variable there.
Adding to what RedGlyph and Ferruccio said.
May be we can do the following to still declare within a conditional statement to limit its use:
if(int x = read(socket)) //x != 0
{
if(x < 0) //handle error
{}
else //do work
{}
}
else //x == 0
{
return true;
}
To complement the other folks' good answers, you can always limit the scope of the variable by braces:
{
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
}
While not directly related to the question, all of the examples put the error handling first. Since there are 3 cases (>0 -> data, ==0 -> connection closed and <0 -> error), that means that the most common case of getting new data requires two tests. Checking for >0 first would cut the expected number of tests by almost half. Unfortunately the "if(int x = read(socket))" approach given by White_Pawn still requires 2 tests for the case of data, but the C++17 proposal could be used to test for >0 first.
This question's answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions.
It seems that
if (x=y) { .... }
instead of
if (x==y) { ... }
is a root of many evils.
Why don't all compilers mark it as error instead of a configurable warning?
I'm interested in finding out cases where the construct if (x=y) is useful.
One useful construct is for example:
char *pBuffer;
if (pBuffer = malloc(100))
{
// Continue to work here
}
As mentioned before, and downvoted several times now, I might add this is not specially good style, but I have seen it often enough to say it's useful. I've also seen this with new, but it makes more pain in my chest.
Another example, and less controversial, might be:
while (pointer = getNextElement(context))
{
// Go for it. Use the pointer to the new segment of data.
}
which implies that the function getNextElement() returns NULL when there is no next element so that the loop is exited.
Most of the time, compilers try very hard to remain backward compatible.
Changing their behavior in this matter to throw errors will break existing legitimate code, and even starting to throw warnings about it will cause problems with automatic systems that keep track of code by automatically compiling it and checking for errors and warnings.
This is an evil we're pretty much stuck with at the moment, but there are ways to circumvent and reduce the dangers of it.
Example:
void *ptr = calloc(1, sizeof(array));
if (NULL = ptr) {
// Some error
}
This causes a compilation error.
Simple answer: An assignment operation, such as x=y, has a value, which is the same as the newly assigned value in x. You can use this directly in a comparison, so instead of
x = y; if (x) ...
you can write
if (x = y) ...
It is less code to write (and read), which is sometimes a good thing, but nowadays most people agree that it should be written in some other way to increase readability. For example, like this:
if ((x = y) != 0) ...
Here is a realistic example. Assume you want to allocate some memory with malloc, and see if it worked. It can be written step by step like this:
p = malloc(4711); if (p != NULL) printf("Ok!");
The comparison to NULL is redundant, so you can rewrite it like this:
p = malloc(4711); if (p) printf("Ok!");
But since the assignment operation has a value, which can be used, you could put the entire assignment in the if condition:
if (p = malloc(4711)) printf("Ok!");
This does the same thing, but it is more concise.
Because it's not illegal (in C or C++ anyway) and sometimes useful...
if ( (x = read(blah)) > 0)
{
// now you know how many bits/bytes/whatever were read
// and can use that info. Esp. if you know, say 30 bytes
// are coming but only got 10
}
Most compilers kick up a real stink if you don't put parenthesis around the assignment anyway, which I like.
About the valid uses of if(i = 0)
The problem is that you're taking the problem upside down. The "if" notation is not about comparing two values like in some other languages.
The C/C++ "if" instruction waits for any expression that will evaluate to either a boolean, or a null/non-null value. This expression can include two values comparison, and/or can be much more complex.
For example, you can have:
if(i >> 3)
{
std::cout << "i is less than 8" << std::endl
}
Which proves that, in C/C++, the if expression is not limited to == and =. Anything will do, as long as it can be evaluated as true or false (C++), or zero non-zero (C/C++).
Another C++ valid use:
if(MyObject * pObject = dynamic_cast<MyInterface *>(pInterface))
{
pObject->doSomething();
}
And these are simple uses of the if expression (note that this can be used, too, in the for loop declaration line). More complex uses do exist.
About advanced uses of if(i = 0) in C++ (Quoted from myself)
After discovering a duplicate of this question at In which case is if(a=b) a good idea?, I decided to complete this answer with an additional bonus, that is, variable injection into a scope, which is possible in C++, because if will evaluate its expression, including a variable declaration, instead of limiting itself to compare two operands like it is done in other languages:
So, quoting from myself:
Another use would be to use what is called C++ variable injection. In Java, there is this cool keyword:
synchronized(p)
{
// Now, the Java code is synchronized using p as a mutex
}
In C++, you can do it, too. I don't have the exact code in mind (nor the exact Dr. Dobb's Journal's article where I discovered it), but this simple define should be enough for demonstration purposes:
#define synchronized(lock) \
if (auto_lock lock_##__LINE__(lock))
synchronized(p)
{
// Now, the C++ code is synchronized using p as a mutex
}
This is the same way, mixing injection with an if and for declaration. You can declare a primitive foreach macro (if you want an industrial-strength foreach, use Boost's).
See the following articles for a less naive, more complete and more robust implementation:
FOR_EACH and LOCK
Exception Safety Analysis
Concurrent Access Control & C++
How many errors of this kind really happens?
Rarely. In fact, I have yet to remember one, and I have been a professional for the past 8 years.
I guess it happened, but then, in 8 years, I did produce a sizeable quantity of bugs. It's just that this kind of bugs did not happen enough to have me remember them in frustration.
In C, you'll have more bugs because of buffer overruns, like:
void doSomething(char * p)
{
strcpy(p, "Hello, World! How are you \?\n");
}
void doSomethingElse()
{
char buffer[16];
doSomething(buffer);
}
In fact, Microsoft was burned so hard because of that they added a warning in Visual C++ 2008 deprecating strcpy!
How can you avoid most errors?
The very first "protection" against this error is to "turn around" the expression: As you can't assign a value to a constant, this:
if(0 = p) // ERROR: It should have been if(0 == p). IT WON'T COMPILE!
It won't compile.
But I find this quite a poor solution, because it tries to hide behind a style what should be a general programming practice, that is: Any variable that is not supposed to change should be constant.
For example, instead of:
void doSomething(char * p)
{
if(p == NULL) // POSSIBLE TYPO ERROR
return;
size_t length = strlen(p);
if(length == 0) // POSSIBLE TYPO ERROR
printf("\"%s\" length is %i\n", p, length);
else
printf("the string is empty\n");
}
Trying to "const" as many variables as possible will make you avoid most typo errors, including those not inside "if" expressions:
void doSomething(const char * const p) // CONST ADDED HERE
{
if(p == NULL) // NO TYPO POSSIBLE
return;
const size_t length = strlen(p); // CONST ADDED HERE
if(length == 0) // NO TYPO POSSIBLE
printf("\"%s\" length is %i\n", p, length);
else
printf("the string is empty\n");
}
Of course, it is not always possible (as some variables do need to change), but I found than most of the variables I use are constants (I keep initializing them once, and then, only reading them).
Conclusion
Usually, I see code using the if(0 == p) notation, but without the const-notation.
To me, it's like having a trash can for recyclables, and another for non-recyclable, and then in the end, throw them together in the same container.
So, do not parrot an easy style habit hoping it will make your code a lot better. It won't. Use the language constructs as much as possible, which means, in this case, using both the if(0 == p) notation when available, and using of the const keyword as much as possible.
The 'if(0 = x)' idiom is next to useless because it doesn't help when both sides are variables ('if(x = y)') and most (all?) of the time you should be using constant variables rather than magic numbers.
Two other reasons I never use this idiom, IMHO it makes code less readable and to be honest I find the single '='to be the root of very little evil. If you test your code thouroughly (which we all do, obviously) this sort of syntax error turns up very quickly.
Standard C idiom for iterating:
list_elem* curr;
while ( (curr = next_item(list)) != null ) {
/* ... */
}
Many compilers will detect this and warn you, but only if you set the warning level high enough.
For example:
~> gcc -c -Wall foo.c
foo.c: In function ‘foo’:
foo.c:5: warning: suggest parentheses around assignment used as truth value
Is this really such a common error? I learned about it when I learned C myself, and as a teacher I have occasionally warned my students and told them that it is a common error, but I have rarely seen it in real code, even from beginners. Certainly not more often than other operator mistakes, such as for example writing "&&" instead of "||".
So the reason that compilers don't mark it as an error (except for it being perfectly valid code) is perhaps that it isn't the root of very many evils.
The assignment as conditional is legal C and C++, and any compiler that doesn't permit it isn't a real C or C++ compiler. I would hope that any modern language not designed to be explicitly compatible with C (as C++ was) would consider it an error.
There are cases where this allows concise expressions, such as the idiomatic while (*dest++ = *src++); to copy a string in C, but overall it's not very useful, and I consider it a mistake in language design. It is, in my experience, easy to make this mistake, and hard to spot when the compiler doesn't issue a warning.
I think the C and C++ language designers noticed there is no real use in forbidding it because
Compilers can warn about it if they want anyway
Disallowing it would add special cases to the language, and would remove a possible feature.
There isn't complexity involved in allowing it. C++ just says that an expression implicitly convertible to bool is required. In C, there are useful cases detailed by other answers. In C++, they go one step further and allowed this one in addition:
if(type * t = get_pointer()) {
// ....
}
Which actually limits the scope of t to only the if and its bodies.
It depends on the language. Java flags it as an error as only Boolean expressions can be used inside the if parenthesis (and unless the two variables are Boolean, in which case the assignment is also a Boolean).
In C, it is a quite common idiom for testing pointers returned by malloc or if after a fork we are in the parent or child process:
if ( x = (X*) malloc( sizeof(X) ) {
// 'malloc' worked, pointer != 0
if ( pid = fork() ) {
// Parent process as pid != 0
C/C++ compilers will warn with a high enough warning level if you ask for it, but it cannot be considered an error as the language allows it. Unless, then again, you ask the compiler to treat warnings as errors.
Whenever comparing with constants, some authors suggest using the test constant == variable so that the compiler will detect if the user forgets the second equality sign.
if ( 0 == variable ) {
// The compiler will complaint if you mistakenly
// write =, as you cannot assign to a constant
Anyway, you should try to compile with the highest possible warning settings.
Try viewing
if( life_is_good() )
enjoy_yourself();
as
if( tmp = life_is_good() )
enjoy_yourself();
Part of it has to do with personal style and habits. I am agnostic to reading either if (kConst == x) or if (x == kConst). I don't use the constant on the left because historically I don't make that error and I write code as I would say it or would like to read it. I see this as a personal decision as part of a personal responsibility to being a self-aware, improving engineer. For example, I started analyzing the types of bugs that I was creating and started to re-engineer my habits so as not to make them - similar to constant on the left, just with other things.
That said, compiler warnings, historically, are pretty crappy and even though this problem has been well known for years, I didn't see it in a production compiler until the late 80's. I also found that working on projects that were portable helped clean up my C a great deal, as different compilers and different tastes (ie, warnings) and different subtle semantic differences.
I, personally, consider this the most useful example.
Say that you have a function read() that returns the number of bytes read, and you need to use this in a loop. It's a lot simpler to use
while((count = read(foo)) > 0) {
//Do stuff
}
than to try and get the assignment out of the loop head, which would result in things like
while(1) {
count = read(foo);
if(!(count > 0))
break;
//...
}
or
count = read(foo);
while(count > 0) {
//...
count = read(foo);
}
The first construct feels awkward, and the second repeats code in an unpleasant way.
Unless, of course, I've missed some brilliant idiom for this...
There are a lot of great uses of the assignment operator in a conditional statement, and it'd be a royal pain in the ass to see warnings about each one all the time. What would be nice would be a function in your IDE that let you highlight all the places where assignment has been used instead of an equality check - or - after you write something like this:
if (x = y) {
then that line blinks a couple of times. Enough to let you know that you've done something not exactly standard, but not so much that it's annoying.
if ((k==1) || (k==2)) is a conditional
if ((k=1) || (k=2) ) is BOTH a conditional AND an assignment statement
Here's the explanation
Like most languages, C works inner-most to outermost in order by operator precedence.
First, it tries to set k to 1, and succeeds.
Result: k = 1 and Boolean = 'true'
Next: it sets k to 2, and succeeds.
Result: k = 2 and Boolean = 'true'
Next: it evaluates (true || true)
Result: k still = 2, and Boolean = true
Finally, it then resolves the conditional: If (true)
Result: k = 2 and the program takes the first branch.
In nearly 30 years of programming I have not seen a valid reason for using this construct, though if one exists it probably has to do with a need to deliberately obfuscate your code.
When one of our new people has a problem, this is one of the things I look for, right along with not sticking a terminator on a string, and copying a debug statement from one place to another and not changing the '%i to '%s' to match the new field they are dumping.
This is fairly common in our shop because we constantly switch between C and Oracle PL/SQL; if( k = 1) is the correct syntax in PL/SQL.
It is very common with "low level" loop constructs in C/C++, such as with copies:
void my_strcpy(char *dst, const char *src)
{
while((*dst++ = *src++) != '\0') { // Note the use of extra parentheses, and the explicit compare.
/* DO NOTHING */
}
}
Of course, assignments are very common with for loops:
int i;
for(i = 0; i < 42; ++i) {
printf("%d\n", i);
}
I do believe it is easier to read assignments when they are outside of if statements:
char *newstring = malloc(strlen(src) * sizeof(char));
if(newstring == NULL) {
fprintf(stderr, "Out of memory, d00d! Bailing!\n");
exit(2);
}
// Versus:
if((newstring = malloc(strlen(src) * sizeof(char))) == NULL) // ew...
Make sure the assignment is obvious, thuogh (as with the first two examples). Don't hide it.
As for accidental uses ... that doesn't happen to me much. A common safeguard is to put your variable (lvalues) on the right hand side of the comparison, but that doesn't work well with things like:
if(*src == *dst)
because both oprands to == are lvalues!
As for compilers ... who can blame 'em? Writing compilers is difficult, and you should be writing perfect programs for the compiler anyway (remember GIGO?). Some compilers (the most well-known for sure) provide built-in lint-style checking, but that certainly isn't required. Some browsers don't validate every byte of HTML and Javascript it's thrown, so why would compilers?
There are several tactics to help spot this .. one is ugly, the other is typically a macro. It really depends on how you read your spoken language (left to right, right to left).
For instance:
if ((fp = fopen("foo.txt", "r") == NULL))
Vs:
if (NULL == (fp = fopen(...)))
Sometimes it can be easier to read/write (first) what your testing for, which makes it easier to spot an assignment vs a test. Then bring in most comp.lang.c folks that hate this style with a passion.
So, we bring in assert():
#include <assert.h>
...
fp = fopen("foo.txt", "r");
assert(fp != NULL);
...
when your at the midst, or end of a convoluted set of conditionals, assert() is your friend. In this case, if FP == NULL, an abort() is raised and the line/file of the offending code is conveyed.
So if you oops:
if (i = foo)
insted of
if (i == foo)
followed by
assert (i > foo + 1)
... you'll quickly spot such mistakes.
Hope this helps :)
In short, reversing arguments sometimes helps when debugging .. assert() is your long life friend and can be turned off in compiler flags in production releases.
As pointed out in other answers, there are cases where using assignment within a condition offers a brief-but-readable piece of code that does what you want. Also, a lot of up-to-date compilers will warn you if they see an assignment where they expect a condition. (If you're a fan of the zero-warnings approach to development, you'll have seen these.)
One habit I've developed that keeps me from getting bitten by this (at least in C-ish languages) is that if one of the two values I'm comparing is a constant (or otherwise not a legal lvalue), I put it on the left-hand side of the comparator: if (5 == x) { whatever(); } Then, if I should accidentally type if (5 = x), the code won't compile.
You asked why it was useful, but keep questioning examples people are providing. It's useful because it's concise.
Yes, all the examples which use it can be rewritten - as longer pieces of code.
I have only had this typo once in my 15 years of development. I would not say it is on the top of my list of things to look out for. I also avoid that construct anyway.
Note also that some compilers (the one I use) issue a warning on that code. Warnings can be treated as errors for any compiler worth its salt. They can also be ignored.
Placing the constant on the left side of a comparison is defensive programming. Sure you would never make the silly mistake of forgetting that extra '=', but who knows about the other guy.
The D programming language does flag this as an error. To avoid the problem with wanting to use the value later, it allows declarations sort of like C++ allows with for loops.
if(int i = some_fn())
{
another_fn(i);
}
The compiler won't flag it as an error because it is valid C/C++. But what you can do (at least with Visual C++) is turn up the warning level so that it flags it as a warning and then tell the compiler to treat warnings as errors. This is a good practice anyway so that developers don't ignore warnings.
If you had actually meant = instead of == then you need to be more explicit about it. E.g.,
if ((x = y) != 0)
Theoretically, you're supposed to be able to do this:
if ((x = y))
to override the warning, but that doesn't seem to always work.
In practice I don't do it, but a good tip is to do:
if ( true == $x )
In the case that you leave out an equals, assigning $x to true will obviously return an error.
RegEx sample
RegEx r;
if(((r = new RegEx("\w*)).IsMatch()) {
// ... do something here
}
else if((r = new RegEx("\d*")).IsMatch()) {
// ... do something here
}
Assign a value test
int i = 0;
if((i = 1) == 1) {
// 1 is equal to i that was assigned to a int value 1
}
else {
// ?
}
That's why it's better to write:
0 == CurrentItem
Instead of:
CurrentItem == 0
so that the compiler warns you if you type = instead of ==.