double as true / false
Asked Answered
C

4

6

Bjarne suggests using the condition in if's as scope restriction. In particular this example.

if ( double d = fd()  ) {
   // d in scope here...
}

I'm curios how to interpret the declaration in a true / false sense.

  1. It's a declaration
  2. It's a double.

Edit: It's in 6.3.2.1 The C++ programming language as a recommendation.

Edit2: templatetypedefs suggestion of pointers, in particular with dynamic casts, might give insight to Bjarnes suggestion.

SteveJessop tells me: - A condition is not an expression it can also be a declaration, the value used, is the value being evaluated.

Conceptacle answered 26/6, 2012 at 23:6 Comment(15)
The answer to this question will answer your question as well.Stipel
I just read the section you're talking about and came away with the feeling that the discussion was more about using it as scope restriction (and gaining the additional benefit of compact code) and didn't have much at all to do with the specific datatype. I think he could have replaced double with int and still gotten his intended message across. Thats just how I read it though.Sites
@Sites Yes, but the use of double is begging an answer. An int declaration is still an anomaly deserving an answer.Conceptacle
The value used to initialize the object is converted to a bool for the if statement to test. Converting a double to bool results in false if the double equals equals zero, and true otherwise. For any type, int, char*, float[], you just have to figure out how that type converts to bool and you'll know how the the if statement does it. Boolean conversion are covered in clause 4.12 of the standard.Albatross
@Albatross My biggest issue with this is really the declaration being an expression. Like int i = float f = double x = 1.0;Conceptacle
@CaptainGiraffe : You keep introducing multiple types in your comments, which the code in your question does not do. int i = float f = double x = 1.0; is nonsensical.Stipel
@Captain Giraffe: the declaration is not an expression. The syntax for a if statement is "if (condition) statement", and the syntax for condition is "expression" or "type-specifier-seq declarator = assignment-expression". double d = fd() is the latter. See [stmt.select] in the standard.Donner
@Stipel My point 1. in the question question raises that concern.Conceptacle
@CaptainGiraffe It's not an expression. The grammar says it's a condition, which can either be an expression or a declaration. But a declaration is not an expression and is not used as one here.Albatross
@SteveJessop Thanks was surprised about the type-specifier-seq declarator. Today I learned. Also thanks bames53.Conceptacle
Whoever writes code like that should be taken out back and shot. Is it so hard to put initialization and testing on two separate lines?Dowser
@Mike: unfortunately, the person who writes code like that invented C++ and you didn't, so I don't think your preferences are going to win out. That said, Stroustrup puts code snippets in italics instead of fixed-width like almost everyone else does. So he doesn't win them all.Donner
@MikeBantegui Well If you want Bjarne shot you might be in for a recursion disaster.Conceptacle
@CaptainGiraffe: There are some instances where it's natural. But using a double like the above is absolutely brain dead. If you need a new scope, write a function. Or throw a pair of braces in there. Both are equally valid.Dowser
@MikeBantegui Not arguing, trying to be clever.Conceptacle
B
6

The code that you're seeing is a specialized technique for declaring variables in if statements. You commonly see something like this:

if (T* ptr = function()) {
    /* ptr is non-NULL, do something with it here */
} else {
    /* ptr is NULL, and moreover is out of scope and can't be used here. */
}

A particularly common case is the use of dynamic_cast here:

if (Derived* dPtr = dynamic_cast<Derived*>(basePtr)) {
     /* basePtr really points at a Derived, so use dPtr as a pointer to it. */
} else {
     /* basePtr doesn't point at a Derived, but we can't use dPtr here anyway. */
}

What's happening in your case is that you're declaring a double inside the if statement. C++ automatically interprets any nonzero value as true and any zero value as false. What this code means is "declare d and set it equal to fd(). If it is nonzero, then execute the if statement."

That said, this is a Very Bad Idea because doubles are subject to all sorts of rounding errors that prevent them from being 0 in most cases. This code will almost certainly execute the body of the if statement unless function is very well-behaved.

Hope this helps!

Biota answered 26/6, 2012 at 23:11 Comment(3)
@Albatross It is almost as if you read my mind. I'd still like a comment on the inconsistency of bool b = double d = fd(); though, but that might be another question.Conceptacle
"and moreover is out of scope and can't be used here" - it's in scope in the else clause, but of course with pointers and smart pointers there's not a whole lot you can do with a variable that tests false, so you don't normally see it used in there. Could assign another value, I suppose. If you used it with a user-defined type that has a safe bool idiom to indicate "failure", then perhaps in the else clause you would interrogate the object why it "failed".Donner
Thanks for pointing out the VeryBad Idea of bit-wise double comparison.Sociopath
D
5

In the example Stroustrup gives, the code in the if block divides a value by d:

if (double d = prim(true)) {
    left /= d;
    break;
}

Division by 0 is undefined behavior, so it makes some sense in this case to test d against the value 0.0 before dividing. Putting the definition in the condition is a convenient way to do this, for the reasons Stroustrup states.

Your code gives no reason why the value 0.0 would be special, and therefore it is not clear why anyone would combine the definition of d with that test. Only use Stroustrup's pattern when "false" values of the type you're defining need to be treated specially. Otherwise just do this:

{
    double d = fd();
    // d in scope here...
}
Donner answered 26/6, 2012 at 23:33 Comment(3)
I just read the section and didn't make the connection -- I thought it was a throwaway example -- good catch. Any insight on the break;?Sites
@jedwards: beyond the obvious (it breaks out of some surrounding loop or switch, not shown in this code fragment) I don't know. Maybe this code fragment is taken from some code used elsewhere in the chapter/book? For that matter, I don't know what the function prim does either.Donner
Aww, I was kind of hoping it was some trick that would intrique me like some of these gems. Thanks!Sites
F
2

the if statement predicates on the value that is assigned to the variable in the assignment expression. If the double evaluates to anything but 0.0 it will run the code inside.

Note that you are not supposed to compare doubles with zero, but it generally works in my experience.

Basically, you shouldn't do this.

The other contributors to this topic have found that this expression is used to exclude the zero case so as to avoid a divide-by-zero. That's definitely smart and such a situation legitimizes this usage as far as I'm concerned (but do consider the confusion such code may cause).

Fillbert answered 26/6, 2012 at 23:8 Comment(5)
Bjarne tells me this is an idiom. Just saying. 6.3.2.1 The C++ programming language.Conceptacle
He may be referring to the fact that the double d is in scope inside (and only inside) the if block. Which isn't necessarily obvious. My point is that there's often no good reason to cast a double to bool. Why a variable of type int is not used, I have no idea. (THAT is a very common idiom)Fillbert
Why on earth aren't you supposed to compare doubles with zero? It works perfectly fine and has well-defined behavior. Floating point math can be surprising, but it's not voodoo and isn't well-served by that kind of advice.Raphael
It's not a good habit to get into (comparing floating point values). Zero may be a special case because for all implementations of a floating point type, 0 is bit-equivalent to a 0 of the equivalent size integer type. But when you've got something like the example given it is not clear whether fd() will return a value exceedingly close to zero (but not actually zero) or zero. If this technique is actually used to exclude the case of zero (so a divide by zero does not occur) this is a good thing to do.Fillbert
Yikes! Never use an integer comparison to test for floating point zero. 0x80000000 is a 32 bit (negative) zero. My point was that there are lots of cases where an unambiguous floating point zero will result from correct code (multiplication by zero is an obvious one). And you can use that to write correct code without fear. Treating FPU logic like voodoo which can randomize your output is just a bad idea.Raphael
B
2

It is both a declaration and a double. This is quite equivalent to

{
    double d = fd();
    if (d) {
    }
}

However, this pattern is worth the small additional syntax to simplify, as it is fairly useful and common. In addition, the transformation is less obvious once you start adding else clauses, as d is out of scope for them.

Also, as others have noted, it's useful in general but FP types in specific have some issues when compared against 0.

Basia answered 26/6, 2012 at 23:11 Comment(4)
This is the first answer I'm happy with. This syntax is all over the place.Sites
My question also adresses does bool b = double d = 0.0;Conceptacle
Are you sure that d is out of scope for else branches?Sites
Just tested it, and d is still in scope in the else branche: ideone.com/V7r4hSites

© 2022 - 2024 — McMap. All rights reserved.