Lambda, closures, surprises

Let’s start with a question. Is the following legal (compile + link) C++ code:

const bool myBool1 = [](){ return false; };

If you said yes, it’s legal, and obviously myBool1 is initialized to false. You’re partially right, but you might be surprised. It is in fact, legal code. However, here’s the surprise. The valiable myBool1 is in fact initialized to true. How can that be?

Let’s unpack that line of code. We initialize a const variable using what looks like to be a lambda expression. This technique is a nice alternative to the ternary operator and quite useful for more complex initialization requiring multiple lines and / or variables. Alas, the person who wrote this forgot the key to this technique. Calling the closure right away. Here’s what the programmer meant to do:

// Correct way to do this:
const bool myBool2 = [](){ return false; } ();

You’ll notice at the end of the lambda definition we call the closure to return the value false.

If you’re a bit rusty on lambda vs closure (it doesn’t come up that often at dinner parties, so you can be forgiven): A lambda is the term for a lambda expression, whereas the term closure is what is generated from a lambda expression at runtime. Scott Meyers has a fairly good description here.

So why did the first expression even compile? Well, the right-hand-side creates a closure. However from the code it appears as though were assigning this closure to a const bool variable. From the standard [expr.prim.lambda.closure / 8.4.5.1 paragraph 7]:

The closure type for a non-generic lambda-expression with no lambda-capture whose constraints (if any) are satisfied has a conversion function to pointer to function with C++ language linkage (10.5) having the same parameter and return types as the closure type’s function call operator. The conversion is to “pointer to noexcept function” if the function call operator has a non-throwing exception specification.

We can see from above, that the closure type have a conversion to function pointer. And function pointers evaluate implicitily to bool. Since a function pointer value of anything other than 0 or a nullptr evaluates to true, we have our reason for why myBool1 is initialized to true instead of our expected value, false.

Leave a Reply

Your email address will not be published. Required fields are marked *