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.

Captain Giraffe
  • 14,407
  • 6
  • 39
  • 67
  • 1
    The answer to [this question](http://stackoverflow.com/q/9833790/636019) will answer your question as well. – ildjarn Jun 26 '12 at 23:09
  • 1
    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. – jedwards Jun 26 '12 at 23:26
  • @jedwards Yes, but the use of double is begging an answer. An int declaration is still an anomaly deserving an answer. – Captain Giraffe Jun 26 '12 at 23:27
  • 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. – bames53 Jun 26 '12 at 23:43
  • @bames53 My biggest issue with this is really the declaration being an expression. Like int i = float f = double x = 1.0; – Captain Giraffe Jun 26 '12 at 23:46
  • @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. – ildjarn Jun 26 '12 at 23:47
  • 1
    @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. – Steve Jessop Jun 26 '12 at 23:48
  • @ildjarn My point 1. in the question question raises that concern. – Captain Giraffe Jun 26 '12 at 23:48
  • @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. – bames53 Jun 26 '12 at 23:49
  • @SteveJessop Thanks was surprised about the type-specifier-seq declarator. Today I learned. Also thanks bames53. – Captain Giraffe Jun 26 '12 at 23:49
  • 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? – Mike Bailey Jun 27 '12 at 00:03
  • 2
    @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. – Steve Jessop Jun 27 '12 at 00:04
  • @MikeBantegui Well If you want Bjarne shot you might be in for a recursion disaster. – Captain Giraffe Jun 27 '12 at 00:05
  • @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. – Mike Bailey Jun 27 '12 at 00:34
  • @MikeBantegui Not arguing, trying to be clever. – Captain Giraffe Jun 27 '12 at 01:05

4 Answers4

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!

OK.
  • 2,374
  • 2
  • 17
  • 20
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • @bames53 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. – Captain Giraffe Jun 26 '12 at 23:23
  • 1
    "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". – Steve Jessop Jun 27 '12 at 09:57
  • Thanks for pointing out the VeryBad Idea of bit-wise double comparison. – OK. Feb 09 '17 at 15:07
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...
}
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 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;`? – jedwards Jun 26 '12 at 23:35
  • @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. – Steve Jessop Jun 26 '12 at 23:36
  • Aww, I was kind of hoping it was some trick that would intrique me like some of [these gems](http://www2.research.att.com/~bs/bs_faq.html). Thanks! – jedwards Jun 26 '12 at 23:39
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).

Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • Bjarne tells me this is an idiom. Just saying. 6.3.2.1 The C++ programming language. – Captain Giraffe Jun 26 '12 at 23:10
  • 2
    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) – Steven Lu Jun 26 '12 at 23:11
  • 2
    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. – Andy Ross Jun 26 '12 at 23:31
  • 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. – Steven Lu Jun 27 '12 at 02:51
  • 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. – Andy Ross Jun 28 '12 at 20:02
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.

Puppy
  • 144,682
  • 38
  • 256
  • 465