All right, welcome again everybody. I'm Arthur O'Dwyer, and this talk is on "Lambdas from First Principles."
This is designed to teach you all about lambdas, and what they are, and how to use them,
and that they are not in fact big, scary, Lispy, functional-programmy things.
They actually interoperate really well with C++. And here's how you use them.
I like to be a little bit interactive, but also not super interactive, with questions,
because we do have — ah, 68 slides, I think — to get through in an hour...
But basically, for questions, if I'm going along and you have a question about something,
and it's a question like, "I don't get what you just said," or "Doesn't that contradict what you just said?",
Please do raise your hand and I'll try to clarify.
If you have a question like, "Okay, I see how that works in that case, but what about the case where it's a variadic template?",
Please don't ask those questions until the end. Because we probably will get to that answer in about five slides.
So. No questions that complexify things. Yes questions if you're confused about a concept.
Because the point of this is really to explain the concepts.
Okay, so, whirlwind tour of C++. Let's start really simple.
Plain old functions.
Because lambdas are kind of like functions, right?
So, ah... yes? [CAMERAMAN] You're good. All right.
Plain old functions. Here's a function. I named it `plus1`. What it does, it takes its argument and adds 1 to it.
Over on the right I'm showing the disassembly. I'm going to be showing some disassembly as we go along.
This is what you would get if you compile it with Clang on — I believe this is OS X, just because that's my platform of choice.
So, x86-64, with the Clang mangling scheme.
How many people are familiar...ish... with name mangling in C++?
Bunches. Okay, good. So you all know what that means.
So that's easy right? It's a function. It's a named chunk of code. The name is some mangled form of the function name.
C++ lets us overload functions! So I can have a function named `plus1` that takes an int;
I can have another function — also named `plus1` — that takes a double.
And then if I call `plus1` with an int, I get one version; if I call `plus1` with a double I get a different version.
And this is indicated at the assembly level by a difference in the name mangling of the function name.
So, two blocks of code, both callable as `plus1` in your C++ code,
but of course they have different name-manglings.
So far, so simple. We all know this.
So C++ also lets us write templates.
So when we want to stamp out copies of a function that all have the same behavior —
— in the previous slide I have these two functions,
one adds 1 to ints, one adds 1 to doubles. If I want one to add 1 to complex numbers, or long doubles, or whatever...
I would just keep adding overloads and overloads and overloads.
But we have the function template which allows us to stamp out copies for any type that we want.
And again, those are instantiated on demand.
They get name-mangled slightly differently yet again.
Notice that the template `plus1` instantiated with `int` is a different entity, indicated by a different name-mangling,
than, in the previous slide, the `plus1` overload that took an `int` parameter.
Things about templates — I'm going to go a little bit into templates and then we're going to drop templates for a bit.
And then we're going to come back to them later.
Let's go a little deeper into templates.
So when I call `plus1(42)`, where `42` is an int,
The compiler can use type deduction to know that, okay, you want the `plus1` that takes an int.
And, well, one doesn't really seem to exist yet, so I'm going to stamp one out for you, by instantiating this template.
When we call `plus1(3.14)`... well, that's a double, so you must want a `plus1` that takes a double.
I'll stamp one out for you — I'll instantiate it.
We can also tell the compiler explicitly,
"Even though I'm passing `42`, which looks like an int..."
"...I do really want the version that takes it as a double; and please convert 42 to a double."
I can explicitly say I want `plus1`, with `double` in angle brackets providing the `T` explicitly.
And, vice versa, there are a couple of contexts which are not function calls
where the compiler will do type deduction for us. Which is actually really handy.
For example, I can take `plus1` here — just the name `plus1`, without saying `plus1` or `plus1` —
And as long as I'm storing it into, let's say, a function pointer that takes an int and returns an int,
the compiler can say, oh, well, you must want `plus1`!
It is smart enough to do that.
You might say, well, what if I took this `plus1` and assigned it into, like, `auto`?
Then how does it deduce what the type of `T` is,
and how does it deduce which `plus1` to use?
And the answer is, it doesn't.
A function TEMPLATE is not the same thing as a FUNCTION, right?
The function TEMPLATE is a source-level construct, and...
if you use it in a context where it's ambiguous, the compiler will give you a diagnostic of some sort.
In this case, Clang complains about "incompatible initializer of type..." and then in brackets it just says "".
Because the type doesn't have a name. It's a template.
All right, one more little thing — the first puzzle. There are three puzzles in this talk.
One more thing about templates before we move on.
Here I have a function template named `kitten`.
It has a static local variable of type `int`, initialized to zero...
...and every time we call `kitten` we're going to `++` that variable. Right?
I call `kitten(1)`, where `T` gets deduced as `int`, and that's going to print...?
..."2". Yeah. Okay. And then I call `kitten` again with `3.14`...
(This is even harder math. I know.) But it's gonna print...?
..."4.14". Yes. Not "5.14"!
Yes! Even though the int was static. Because it is a DIFFERENT static int.
Here's the codegen. We have two instantiations here, with different name manglings and different code.
You notice that the one for `int`, you know, it's involving lots of sort of "inty" operations.
And here's the one for `double`, and it — well, it does some stuff with the XMM registers, the `double` registers.
So they have different codegen. And, going along with that, each of them gets its own set of static local variables and so on.
Here we have the `x` for the `double` version, and this is the `x` for the `int` version.
They don't share memory.
They're two completely separate variables because we have two completely separate instantiations of this template.
This is made a little bit clearer by — like, you might say, "Well, that just seems dumb."
Like "Why did the standards committee make that decision? Why DON'T we have one variable `x`?" That's what I want, right?
Well, not really. Because what if, instead of `int x` right here...
We made it `T x`. What if we made it something template-dependent?
You can do that, right? You can have a local variable of template-dependent type.
For example, in this case, when I have `kitten`, `T` is `int` and `x` is of type int.
When I have `kitten`, well, my `T` now — my `x` now — for this function need to be of type double.
So it's kind of obvious at this point that we're going to NEED two different variables.
The int variable for the int version, the double variable for the double version.
So. Templates and statics. Little pitfall. Little sneaky there.
All right, we'll come back to templates later. Let's go back to what other crazy things can we do with functions in C++.
Well, you can make member functions! I can have a class. I'm going to call this class `Plus`.
It's going to have a data member named `value`.
You can instantiate it — or, uh, you know, you can get an instance of this class —
by calling its constructor...
And it's going to have a member function which I'm just going to call `plusme`.
If I have a `Plus` object, I can call, like, `myPlusObject`, dot, `plusme`, give it an int...
...and it gives me back the sum of the int I gave it and the int that was given in its constructor.
Another little digression. I love digressions.
When I call `plus.plusme` of, let's say, `42`...
How does the `plus` object know to go call this code?
Like, this object has to KNOW, "oh yeah, when you call `plusme` on me, that means this code here."
Is there some sort of pointer to that code somewhere?
No! C++ is not Java. Thank God.
In the Java approach, there would be that pointer.
There, every object is allocated on the heap, and has a vptr that points to a table of "here are all the behaviors for this object."
That's nice in that we can now pass a pointer to this object around and the object knows its own behaviors.
It is NOT so nice in that now we've got, like, 1, 2, 3 pointers here...
...that you have to follow whenever you want to do something with this object.
C++ lets you do this via virtual member functions, which we're actually not going to talk about...
But of course in C++ it just looks like this.
On the stack we have our `Plus` object just sitting right there. It doesn't need to be on the heap.
We don't want to use the heap. We don't want to use dynamic memory management.
And the function, the implementation of the function, is just sitting there in the static code. And there is no linkage between them at all.
There's just sort of an implicit — like, the compiler just knows that when I'm doing something on a `Plus` object,
of course I'm going to use `Plus`'s member function.
So yeah, C++ lets us get rid of all this dynamic memory management and whatnot that you have in Java, and so on.
But you knew that, because you're here!
All right, so, class member functions.
Now this member function, I just named it `plusme`.
But let's say this was the ONLY thing that I could do with a `Plus` object...
C++ also gives us the ability to do operator overloading.
So I could actually overload the function-call operator on my `Plus` object, right?
Exact same codegen as before. The only difference is that the name-mangling has changed a little bit...
This name mangling right here has changed from being the name of the class and the member function, to being just `cl`, which stands for "call."
So let's see.
So I can overload an operator. And then when I call it — you'll notice that the call changed...
From `plus.plusme(42)`... now I can just say `plus(42)`...
And the compiler will know, oh, okay, you mean to call `operator()` here.
And of course then it puts the call to `operator()` in there statically, because: static typing.
Now we can do something kind of nifty!
We take this code I just wrote...
where we have this class, and the call operator, and the constructor, and where we create objects of this type using the constructor...
The syntax highlighting that I've used here is, in red, in bold, I've put the irreducible complexity.
The stuff that — well, this is just what it does, right? I can't get around the fact that there is a member named `value`...
or that my `operator()` takes another int, or that what it does is add them together. Right?
That's the fundamental semantics of my class.
But everything in the in the light green there is just boilerplate. I don't want to have to write that. That's a lot of boilerplate.
So let's just get rid of all the green!
Okay, all the green is now gone, except for a lot of brackets.
Actually, one of each kind of bracket.
It's a very fair language.
And a little `=` sign there.
So now we're saying very succinctly what we want to say about this entity in our code.
We've thrown away all of that reducible complexity
about what is the name of the class and the fact that you have to write out its constructor,
and now we're just saying it's got a member named `value`
Here's the initial value for `value`, by the way. We're going to pass in `1`.
But we could pass in any expression that we wanted, of course.
And it has an argument, and we have to give the argument's type...
and we have to give the argument a name, because we're going to refer to it later, right? So we give it a name, `x`...
and then we say here's the behavior.
And that's it! And we wrap that all up, and that is a lambda expression!
And we use it exactly the same way.
You'll notice that the `assert` down at the bottom, that's checking the behavior of our object `plus`,
where before it was constructed explicitly as a `Plus` object...
Now it's just constructed as a lambda expression, which produces one of these anonymous class objects,
and it behaves exactly the same way.
And the codegen is exactly the same! Same implementation.
Except that now of course we don't have a capital-P `Plus` object anymore.
Because we just removed that name from our code. There's no more names in our code.
So the compiler makes up a name for this class.
And, at least in Clang's mangling scheme, it just says `$_0`.
"Dollar sign, underscore, integer" is the the naming scheme for these anonymous objects.
Let's see... so now, on the stack, instead of having `plus`, a `Plus` object, I have `plus`, a `$_0` object.
And instead of calling `Plus::operator()`, it's calling `$_0::operator()`,
indicated by the mangling down there in the fine print that I don't know if you can read in the back
And the codegen is exactly the same.
Oh, by the way, I do want to point out that this `value=1` syntax...
This is C++14 "generalized lambda capture", also known as "init-capture."
I like it, for reasons you're about to see. But be aware that if you're on C++11, you don't actually have this syntax.
[INAUDIBLE FROM AUDIENCE]
You just assumed you knew everything about lambdas... well, haha! you don't! —anymore. Yes.
For almost two years now, you don't know everything about lambdas.
Before that you did though
And so this gives us the ability to have sort of Lisp-style, functional-programming-style closures...
without heap allocation and without garbage collection.
Because you saw we just built that out of the building blocks that already exist in C++, right?
I mean, we can all write code that uses classes and so on — allocating them on the stack, RAII.
We know how to write that kind of code without invoking the heap, and garbage collection,
and dynamic typing, and so on and so forth
So, since I can take that code — that we had — and remove all the boilerplate and get a lambda,
obviously we can also implement lambdas without garbage collection and dynamic typing and so on and so forth.
So those two things do not go hand-in-hand as you might think if you look at, you know, C++03 versus, like, Scheme,
and said, "oh, I wish I had lambdas, but I can't get them without this other baggage."
Well, yes, you can!
Here's an example of how to use them...
Let's say that I want to `std::sort` this vector of objects by some property...
I can make myself up a little closure object here as a lambda expression.
I'm going to take this `prop` that was passed in by the user —
— and yeah, I'm passing things around by value. Meh.
That's probably okay. Move semantics, right?
I'm going to capture that property: I'm going to make a copy of it inside my closure.
And now that I've captured that thing — which I'm naming `p` here for convenience. It's `prop` outside but it's `p` inside —
just to make sure we know what we're talking about —
inside the lambda I can say, you know, I'm given two objects and I compare that field of them.
And then I can take this `pless` object and I can pass it to `std::sort`,
and `std::sort` will sort things based on this predicate.
Does that all make sense?
I think we're about to see an example of that. ...We are! Okay.
So here's what we might have coming into the function.
I have my `prop`, which is a `std::string`, and so it controls some "hello" out there on the heap...
which is the name of the key I'm sorting by. So maybe "hello" wasn't the best example.
And then I make my closure object — so that's going to be a `$_1` object —
And it is going to have inside it — captured — it's going to have a member variable which I've named `p`...
that's that `p` right there [in pink]...
and it's going to be initialized to a copy of `prop`, so it's going to copy out the "hello" right there.
And that that's what it's going to look like.
So, again, the lambda itself is not living out on the heap anywhere.
There's the heap involved in this case because we're controlling std::strings.
And again, there's this sort of magical compiler static-typing linkage between these things,
that is not represented in the runtime, at runtime.
Okay, so let's see some other alternative syntaxes for this!
So I said this was the C++14 generalized capture. In C++11 you have a more restrictive version.
You can't give things different names, but...
you CAN say, "I want to capture the local variable in my outer scope named `prop`..."
"I will capture it with that SAME name `prop` inside."
But notice that `prop` inside refers to that pink `prop` that I captured. Not to the `prop` in the outer scope.
So you can write that in C++11.
Also C++11 — and of course also in C++14 you can continue to do that, but I don't really think you should —
I don't know. I like explicit names.
So we can also just say, "I'm going to capture everything. Everything I use inside, that you don't know what it is —"
"Go look for it outside, and make a copy of it, please."
So you can give a "default" capture semantics, a "capture-default."
This, ah, many people consider to be a bad idea in general.
It's certainly good that it's in the language! There are things you can do with it — you can put it inside macros and so on — that makes it very nice to have it in the language.
But probably not something you should do a lot.
Oh, and the other thing going on here, by the way — we were talking about doing all of this without garbage collection —
you can see that once I've made this copy, the original `prop` can even go away
and I just pass my lambda around.
There's no dangling references, or even reference-counting going on.
It's just "well, I made a copy. Now it's mine."
So I can get rid of the original, if I want.
But what if I WANT to capture a reference?
Because this `prop` that I'm making the copy of — this is a really big box.
I'm making this big copy. I don't want something this heavyweight. I want a nice lightweight pointer.
So I want to be able to capture a reference. So what do I put
in my capture-list here
if I want to capture a reference?
Well, I COULD do that [i.e., capture a std::reference_wrapper instead].
[INAUDIBLE FROM AUDIENCE]
Yes. Yes. You COULD do this, but you SHOULDN'T do this.
What you really should do — the built-in syntax for it —
is, you put an ampersand `&` in front of the name of the thing you're capturing,
and that magically — well, okay, it's not magic, but it's a special case — where the compiler will say, "Oh, okay, now you want a reference to it."
"Okay, I'll give you a reference."
[AUDIENCE] Hopefully, this is a [fine?] question, uh, I don't know what is the `std::ref` type?
What is `std::ref`? `std::ref` is a function that gives you back a `std::reference_wrapper`.
[AUDIENCE] So is that syntax in C++14 then therefore using `auto`, so that it knows it's a reference type?...
[AUDIENCE] ...I thought `auto` drops reference types.
Ah, a `reference_wrapper`... Yes, it is not an actual native reference. It's not an actual `T&` variable.
A `reference_wrapper` is an actual value type that you can copy, and assign, and whatnot.
But it behaves like a reference. So that when you use it — it basically just has an implicit conversion to `T&`, I think.
[INAUDIBLE FROM AUDIENCE]
"There is an implied `auto` in front of the `p=`." Yes. Basically yes.
However, if you think of it that way, then you'll start thinking, oh, well I should be able to put `&&p=prop`, and that should capture an rvalue reference
No! That DOESN'T work!
"I should be able to put `*p = prop`, and that should capture an auto pointer."
No! THAT doesn't work!
It's a very special syntax.
If you don't put anything it captures by value; if you do put a single ampersand it captures by reference; and that's it.
Those are the two kinds of things.
And similarly... We saw that you can capture everything by value by putting the `=`. You can also capture everything by reference by putting just the `&`.
And that means, "Anything that I use in my inner scope that I haven't defined in my inner scope..."
"...go look for it in the outer scope, and if you find it there, capture it by reference."
And I also skipped over the slide where in C++11 you can capture named local variables, with the same name, inside, by reference as well as by value.
But beware of dangling references! Right! If you capture everything by reference,
and then you get rid of the referred-to stuff,
then you have dangling references, dangling pointers.
Which we all know from this morning's keynote are terrible.
So be very careful when you do this.
Yeah, the dangling pointer problem is solved [by shared_ptr]. Yes. So don't worry about it! [LAUGHTER]
Basically capturing everything by reference is really really great and convenient
for lambdas that you know are only going to go "down" your call chain and never escape upward into your caller.
Then capturing everything by reference is just the simplest thing to do.
And if you're not doing that, you probably shouldn't be using a capture-default at all. You should just spell out explicitly "I want to capture this and this and this."
By the way! If you literally capture `this` —
let's say, by copy — What are you capturing?
Capturing a pointer by copy. Yeah, so it will look as if you're capturing it by reference, basically.
And it will do this even if you're not writing `this` in your code...
But you just say "I want to capture everything by copy" and then I refer to a member variable — `mymember` or whatever —
The compiler will say, "How do I get `mymember`? Well, that's really `this->mymember`, so I'll capture `this` by copy."
And you'll think you're getting a copy of `mymember`, but in fact you're not — you're getting a copy of the `this` pointer.
I wish I had a slide on that.
Okay, so just to sum up default capture modes...
Here I have a couple of factories called `increment_by`.
You pass in one integer — this is basically a factory for our `Plus` lambda, right?
You pass in the number you want to add to things...
So for example I can call `increment_by(5)`. That gives me back a lambda, which I'm naming `plus5`.
And then if I call `plus5` on 2, I get 7.
The `GOOD_increment_by` captures the `y` by copy, and it all works great.
`BAD_increment_by` captures that `y` by reference,
so that the lambda that I'm returning back out of my scope has a reference to a local parameter (local variable).
Returning a reference to a local variable obviously doesn't work.
All right, another couple of grab-bag features of lambdas.
They are convertible to raw function pointers as long as you don't capture anything. That's super useful.
If you DO capture something, then of course you can't. Because you can't turn an arbitrary— going back to our original capital-P `Plus` object,
I can't turn a capital-P `Plus` object into a function pointer, because, where do I put the data?
But, if I don't have any captures,
then my `Plus` object is basically just a handle to its `operator()`,
which takes a `this` pointer, but it doesn't DO anything with it because the `this` doesn't control any data.
So really it's just a pure behavior. It's just a pure function.
And so in that case the language actually allows us to convert it to a function pointer.
Variables with global scope are not captured.
There's a puzzle about this later. This is the spoiler.
And lambdas may have local state — but not in the way you think.
Here's a puzzle. Puzzle number two.
What have we got here? We've got a global variable `g` which I initialize to 10.
And then inside `main`, I'm going to set `g` to 20.
And then I'm going to call my two lambdas here, which, in the traditional style, I'm naming after cats.
We have `kitten`, that captures EVERYTHING by value; and we have `cat`, which captures only `g` by value.
What do we think this is going to print?
I heard "11, 11"; I heard "1, 11."
I heard "21, 11."
I also heard "21, 21." Is that all the combinations? I think we've completed the square.
The answer is "21, 11." Here I've color-coded what's going on.
So, if you can read my mind with the color coding...
So the global `g`, which is purple here... Globals are not captured!
Right? Because why would you need to capture a global? It's global! Everyone can see it!
[AUDIENCE] Because you told it to!
"Because he told it to."
Well, no, I just told it, "By default, if there's something in my scope that you don't see inside, just assume that it's in the outer scope."
But `g` — of course I know what `g` is. `g` is the global variable `g`.
So `kitten` doesn't need to capture anything. It uses `g` but doesn't need to capture it. It's global. It knows `g`'s address.
Its address is never going to change.
So `kitten` is actually going to basically track updates to our `g` as we make them.
So that's why it gives us "21." By the time it actually tries to use `g`, `g` has already changed to 20.
Now, `cat`, on the other hand — we explicitly told it, "I want you to have a non-static data member..."
"...in this `$_1` class — I want it to have a non-static data member whose name is `g`..."
"...and I want its initial value to be the result of the expression `g`."
The result of the expression `g` is 10, right now, when I create `cat`.
So it gets a local `g`, controlled by the `cat` object, with value 10;
and 10 plus 1 is 11.
[INAUDIBLE FROM AUDIENCE]
Yep. C++11 either doesn't have this problem or has it worse, depending on which compiler you use.
In C++11, if I use the the format where I don't have the `=` sign and I just say, please capture `g`,
that is a constraint violation and should be diagnosed.
As of this writing, Clang diagnoses it with an error;
GCC diagnoses it with a warning and keeps going!
And... I do not remember what it does.
So be aware that even if you name the global, if you use C++11 style, (A), that is a constraint violation and requires a diagnostic,
and (B), you may not get a diagnostic.
[INAUDIBLE FROM AUDIENCE]
Is it the same thing if you capture by reference?
Well in that case it definitely wouldn't matter, right? I mean if you're capturing it by reference then you'd know you're not getting a copy...
So yes, it would still be a constraint violation,
but I can't imagine anyone thinking that they were going to get any behavior different from what they got. So.
All right. Time to revisit statics!
Here I have a kitten factory.
You pass it in a value that you would like to add...
The actual behavior of this lambda, this is going to add up a bunch of stuff, right?
Two of the things that it's going to add up are the value you pass to the factory (`c`) and value you pass to the constructed lambda (`d`).
Also to that we're going to add the values of two statics, which we're going to be `++`ing every time you call the lambda:
one that's static in the factory (`a`), and one that's static inside the lambda itself (`b`).
And now we're going to make two kittens one with value 1, one with value 2, and we're going to call them...
We're going to call the first one twice and then we're going to call the second one twice. And the question is, what does this print?
And I don't even want to think about this, but if someone out there does, just shout out the answer and I'll tell you if you're right.
Here's the answer. All right.
So what's actually happening here?
Well, we're making a `kitten` of `1`. So we're going to come in here,
and the `a` inside `make_kitten` is going to start with value 0.
And it's going to return a new closure constructed from this lambda-expression.
So it's going to be an instance of a `$_0` class, for some value of `$_0`.
And the `operator()` of this class is going to have the following behavior.
It's going to have a copy of `c`. Because we're capturing everything by copy.
We're going to increment the `a`, which is just global — like, there's just one of it, inside `make_kitten`, right?
We're going to increment the `b`, which is static local inside this scope.
And then we're going to add the `c` and the `d` to them.
(This is already confusing me. Too many variables.)
(Also, post-increment. Why did I choose post-increment?)
But the gist of this is that of course there's only one static `a`, right?
Because it is in the scope of `make_kitten`, and there's only one `make_kitten`.
But how many `b`s are there? Do we have one `b` or two `b`s?
The answer is, we have one `b`. Because `b` is static in the scope of this anonymous class's `operator()` function!
That `operator()` function has a static local variable `b`.
So it's going to look kind of like this. (Oh, this doesn't show up the way it showed up before. Just pretend this is all on one line.)
So we have a single static `a`...
...and we have a single static `b`...
whose name-mangling indicates that it's the `b` that belongs to the `operator()` of class `$_0`.
(Let me try to resize the font to make that name-mangled thing clearer.)
We have our kittens `k1` and `k2`. They each captured a `c`.
And they're statically — through the magic of static typing —
they use this code which refers to the `a` inside `make_kitten` and the `b` inside the call operator.
But there's only one `b`, and that's why they see each other's updates to `b`.
Because there is only one call operator. Yes, the `$_0` class has a single call operator. Which has a static local `b` that it refers to.
But only one of them.
So, statics inside member functions, uh, behave the way that you might already know they behave.
[AUDIENCE] Is it appropriate in this case to solve the conundrum by saying, this is created at compile time...
[AUDIENCE] It's not gonna create a different "stamp," as you called it, just because you called it again a little bit later.
[AUDIENCE] So it's the same one because it was stamped once at compile time and its linkage — I mean those calls are static calls, they're not dynamic.
[AUDIENCE] We figured that out in some detail previously. so there's just one of those methods... [INAUDIBLE]
[ARTHUR] Yes. I mean, I would go with that.
But I want to say that it's not super obvious that that's what should happen, because...
This lambda-expression here — we do say very explicitly, "I want there to be a static int named `b` inside this scope."
The committee COULD have decided to make it so that when you declare a static locally inside this lambda thing, that that really means that we get one PER closure.
Like, each one gets their own. And it's initialized to 0. "Why didn't they do that?" you might ask...
Because we already have a way to do that!
So here's the situation we have now, right, where each of them gets their own `c`, because they captured it,
but they share a reference to `b` because there's only one `b`.
And then if one of them modifies `b`, the other one sees the update.
And that that's not what we really wanted in this case. Let's suppose that's not what we want in this case.
What we wanted — what we thought we were getting and it turned out we weren't — is that we thought we were getting a `b` in each of these closure objects.
So how do we actually achieve that?
What is the syntax provided for that?
Well, it should be pretty obvious that what we're doing with `b` here, putting one in each closure object,
is exactly the same thing that we had done with `c`, right?
So we should be able to use the same syntax that we used to get the `c`, to also get a `b`.
So all we need to do is just capture something, and we'll name it `b` — because why not? — and we'll initialize it to 0,
and because we're going to modify it, we'll have to add the `mutable` keyword, to make sure our lambda can modify its captures.
But there you go. If you want some extra state per closure object, you just...
stick it in the capture-list, because the capture-list is where your member variables of this anonymous class go.
[AUDIENCE] Could you also have declared it inside the braces but without the `static` keyword?
Could you also have declared it inside the braces without the `static` keyword?
Yes, but then it would be a stack variable. It wouldn't hold its state.
[INAUDIBLE FOLLOWUP FROM AUDIENCE]
This `b` is not on the stack! This — well, I mean, okay, it says "stack" right here, but —
They're inside other things that can then be copied and put somewhere else, or passed as return values, or — passed up or down.
But you can't do this with C++11 syntax as written.
What you would have to do is create a local variable named `b`, initialize that to 0, and capture it by name. Yes, it's ugly.
But that's why I love the generalized capture syntax!
[INAUDIBLE FROM AUDIENCE]
Could `b` be something other than an int? Sure. It could be, you know, it could be a `std::string`, it could be an arbitrarily complicated class.
Do you need to say the type of `b`? No. In fact, you can't.
The only thing you could do there is say `b=`... and then spell out the type.
Like, I'd say, `[b = int(0)]` or `[b = std::string("hello world")]`.
This is similar to... I know Herb Sutter has this idea of "Almost Always Auto," which is catching on to a certain degree,
and this is one place where it is "Always Auto." You don't get a choice.
There's no way to specify the type on the left-hand side of the `=` sign.
If you want a specific type, you must either write an expression that gives you that type, or...
...or write an expression that gives you that type. By means of a cast.
Okay, so there we go. Per-lambda mutable state!
So I just snuck this `mutable` keyword in here.
If we're going to `++b`, we need to say `mutable`. What is that actually doing?
Notice that `mutable` is not going on `b` itself, and there's no way to do that.
It's going in the same position that you would expect to find...
...the `const` qualifier in a member function. I say, `operator()() const`.
And then in a lambda I say `[something]() mutable`.
The reason for this is that lambdas, by default, their `operator()` is always const.
It was decided that, generally speaking, you don't want to modify your captures.
If you are modifying your captures, that's probably a bug. You would really like us to warn about that.
So your `operator()` on your anonymous class, `$_0` or whatever, is always `const`...
unless you say `mutable`, which makes it non-const.
So everything has shifted one level down, "const-ward," from what you would expect.
Saying `mutable` gives you an `operator()` on this anonymous class object which is not `const`...
and therefore it CAN modify the captured member variables.
[INAUDIBLE FROM AUDIENCE]
"How come `a` wasn't captured by copy?"
Right. And we said, "capture everything by copy,..."
"...IF you can't already see it." Right? We had to capture `c` by copy because there's no `c` in the scope...
but there is (again) only one `a`.
Globals and statics in general are not captured by copy, because we already know their address.
When you say `a`, we know you're talking about this `a`. We know we don't need a copy of it. It's right here. Its name is `a`.
And so that's why it wasn't captured.
All right, so, generic lambdas! Generic lambdas from scratch! That was the whole point of the talk, right? Okay.
Let's go back to the beginning.
Class member functions! Class member function templates!
Let's go back to our `class Plus` with a `plusme` member function.
Let's now make that a template. Now instead of taking `int x`, we're going to take `T x` for any type `T`.
And we're going to return a `T`.
Really I guess we should be returning `auto` and just let type deduction sort of figure out what the type is. Well, `T` plus `int` gives you `T`, right?
Okay, so now we have a template, and we can instantiate this template.
We can say — once I have a `Plus` object, like `Plus(1)`, I can say `plus.plusme(42)`...
That will instantiate `plusme` with `T=int`...
And we'll get something that name-mangles to something like this, and those integer operations.
I can say `plus.plusme(3.14)`; type deduction will say "aha, `T` wants to be `double` here..."
I will instantiate — stamp out an instantiation of — `plusme` for a `double`.
And we'll get some `double` codegen.
Okay, looks great, right? Good. Okay.
Okay, what did we do next?
Call operator! Boom. All right. Replace `plusme` with `operator()`.
Now the name mangling has changed a little bit. We have `cl` there, where before we had `plusme`.
The way we call it has changed a little bit.
Now we make something kind of nifty...
Here's the reducible complexity and the irreducible complexity.
Irreducible in red. The reducible complexity in green.
And we're going to say "Make me a `Plus` where the value is 1"...
And then I can do the same thing I did before...
Let's take all that boilerplate in the green and let's get rid of it
It all goes away.
And now I have the same thing I had before.
Before, I had an instance of a `Plus` class where its `operator()` was a template.
Now I have an instance of an ANONYMOUS class where its `operator()` is a template.
This is C++14 at this point, yes.
If you want it in C++11, you write this. [LAUGHTER]
And I learned at lunch today that there are places in Boost that really expect something that behaves like this.
And so C++14 is great, because now you can pass them nice simple lambdas instead of having to write your own templates.
So here we go — all the same stuff as before.
You say, I want every instance of this closure object to have a value...
I will initialize it to — in this case — `1`.
And I have an `operator()` that has one argument. I call it `x`. But what is its type?
Its type is... `auto`.
Which is the new magic catch-all keyword that we use, every time we don't know what other keyword to use.
It's replaced `static` in that respect. [LAUGHTER]
So you say `auto x`, and that means,
"This is a template; figure it out."
You could also say `auto *x`, or `auto &x`, and that would mean "This is a `T*`, this is a `T&`; figure it out."
But in general we would write just `auto`,
or we would write `auto&&`, which would turn into a `T&&`, which is a forwarding reference.
for "perfect forwarding."
All right, so. Generic lambdas.
All right. Here we have another puzzle!
I have a `kitten`. It's a generic lambda, so it takes `auto t`.
And it has a static local named `x`, initialized to 0, and I return `(++x)` plus the value you give it.
So `kitten(1)` is...? "2", right. `kitten(3.14)` is...?
"4.14", right. For exactly the same reason as in puzzle number 1.
(Did I forget to change that slide? I did. "Puzzle #1 redux.")
Right. So now we're circling back to templates, if you didn't notice already.
So yeah. We have ONE ENTITY here. Its name is `kitten`.
It is no longer a template itself. It's not an overload set. `kitten is a real thing, of type `$_0`.
It's an object. I can pass it around. We know its type.
But because its `operator()` is a template, whenever we're using `operator()` we're actually instantiating a template,
and every instantiation gets its own set of function-local statics, for example.
So that's why we get this behavior.
[AUDIENCE] Maybe you're going to answer this, but, can you specify, then, on the left call, which one you want, without just doing a cast on the parameter? Can it have angle-bracket syntax on it?
"Can I use angle bracket syntax to say `kitten` or `kitten`?
I believe not, but I am not sure. I don't actually know. [NOTE: No. You can say `kitten.operator()(42)` but please don't.]
And if you can't, it might be coming, I'm not sure?
It seems reasonable... ish... but probably not.
Because remember, `kitten` itself is not a template, right? So `kitten` is definitely not a thing.
[AUDIENCE] Maybe you could cast it.
Right. I mean, I believe that I could say, like, I'm going to get the address of `kitten.operator()`... I don't know.
There's probably some syntax to make that work. But it's completely ugly, I'm sure.
So, in general, no.
[INAUDIBLE FROM AUDIENCE]
"Can `auto` be used anywhere?" ...No.
Like, can you have, like, a `vector`? ...No.
You can say `vector` and have it deduce the `T` for you,
and you can say sort of similar things, like `auto*` and `auto&&`,
But I don't think you can say `vector`.
And I know there's been a proposal for C++17 to let you say that, but I don't know what happened to it — where it is.
[INAUDIBLE] "vector of decltype..." meh. Whatever.
All right, so, generic lambdas!
Generic lambdas behave in this confusing way, just like templates, because they are templates! Templates under the hood.
All right, uh, we have ten minutes left...
I think we can get through all of the slides... and maybe not the bonus material,
but, hey, that's why we have lightning talks, right?
Let's do variadic function templates, just to prove that we can.
Variadic templates, right? Yeah? Okay.
We have a `class Plus`. It has an `int value`. It has a constructor. It has an `operator()`...
Which is a template which takes an arbitrary number of parameters...
...of types `P`, where `P` represents a parameter pack of types.
And it's going to return the sum of all of those and also the `value`.
So, let's see... right.
So I can say `plus(42, 3.14, 1)`, and that should give me... [COUNTING] ...it should give me 47.14.
Or I could say `plus("foobar", 2)` and that should give me...?
Because `char*`. All right.
All right, so we take the code that we wrote before...
They have the reducible complexity in green; we have the irreducible complexity in red...
We replace all the green with one pair of brackets each...
And you'll notice that we took the `P`, which was a parameter pack — capital `P`, I mean — that disappeared.
Instead we just say `auto`, and that means "I am a template. Figure it out."
And `auto...` is a parameter pack of "I don't know. Figure it out."
So here I have a generic variadic lambda, which takes any number of parameters,
and it's going to take them by value, or by copy,
because I said `auto...` and not `auto&&...`.
And it's going to add them all up.
Also, in C++11 syntax, I can capture a parameter pack. So— we talked about— so this is going to be confusing because I'm doing them right after each other, but—
Here's how I make a variadic template lambda, where the argument list is a parameter pack.
But, flip side of that, I might want the parameter pack in the other set of brackets, right?
I might actually want a non-variadic lambda that captures a whole parameter pack that was passed to the creator of this lambda.
I might actually want both. That's also fine. But in this example — this is going back to our `sort_by_properties`.
So I have a vector of objects, and I want to sort them now not just by one property, but by a bunch of properties.
Possibly empty, actually; but we're going to pretend that the empty case doesn't exist.
and I'm just gonna pass in a whole bunch of strings. I'm going to say `sort_by_properties(v, key1, key2, key3)`.
So there my typenames deduced by `P` are going to be, like, <:string std::string="">.
`props` is a parameter pack representing those three strings.
And I can capture that whole pack, by copy, in my lambda `pless`,
and then I can expand it back out again, when I need it, inside the lambda.
And look at that. Lexicographic sorting, in like one line of code.
So that's nice.
Now, you might say... uh...
Oh. We're gonna get to that in a moment.
Slight digression: "Capturing by move."
Here is our original example, where I'm capturing the string by copy,
but copies do take a lot of memory, so maybe I just want to do a `std::move`.
I can do that in the generalized capture, init-capture, syntax in C++14.
Can I capture a whole parameter pack by move? Can I say this?
Big ol' nope.
Can you do `std::forward`? Big ol' nope.
And there are good technical reasons for this. If you google it, there's a Google Groups post by — I think Richard Smith? Someone who knows —
that explains, well, this is really tricky, because you can't have a non-static data member that is a parameter pack. That doesn't make sense.
Yeah, so you can't do this yet, but maybe it's coming? someday? I wish? Because that would be nice.
What you can do instead is, make a `tuple` out of your whole parameter pack... move things into the tuple... and then `std::move` the tuple into your lambda...
...and then in your lambda you can unpack it. Which I didn't even write, because — like —
because I don't think that that's possible in less than about ten lines of code and a helper function.
So. Can't do that.
And I don't think we're going to talk about `std::bind`. So you might as well ask your questions.
In the back.
[INAUDIBLE FROM AUDIENCE]
"Can't you do that with `std::apply`, from the experimental extensions TS?"
If I were only calling a function on the parameter pack, yes. But you'll notice that I wasn't just calling a function on it
I was taking the parameter pack and doing `a[p]` for every `p` in the parameter pack, and `b[p]` for every `p` in the parameter pack.
So, I could almost— I could use `std::apply` with this, actually. All I need to do is call the "std::square-bracket" functor.
You know, like `std::less`, and `std::plus`... there's one of those for every operator. So all I need to do is call "std::index"...
which doesn't exist.
They just got around to adding `std::bit_not` in C++14.
Eventually they will work their way through all of the operators. But they haven't gotten to square brackets yet
[AUDIENCE] Sorry about the basic question. This is going way way back, but I didn't want to interrupt your flow.
[AUDIENCE] When you do something like `a[p]`, where `p` is part of the captured `props`, what does that evaluate to?
[AUDIENCE] In the earlier example, it was the string "hello". How do you know what `a["hello"]` would be?
"How do I know what `a["hello"]` would be?"
[AUDIENCE] So it's some address of an object `a`, and then you're taking the address of that and "hello"?
Oh. `a` here is a reference to an `object`, which is a `std::map`.
So it's using the map index operator to say "give me the value associated with the key 'hello'."
[AUDIENCE] Oh, I see. Okay.
And I heard someone ask, what is `std::tie`? `std::tie` is like `std::make_tuple`, but it takes references instead of making copies.
So what `tie` is doing here — `std::tie`, but there wasn't room on the slide to write it —
is basically expanding `a[x]` for all `x` in `props`...
so that gives us `a["hello"], a["world"], a[`... for all of the `P`s that you passed in...
...taking references to all of those, and wrapping them up in a tuple.
On the right-hand side, we do the same thing for the `b`s. And then we use tuple's `operator<` to do a lexicographic comparison of those two tuples.
I think we're probably out of time. ...Well, all right. One more.
`std::experimental::apply` takes a function and a tuple, and calls that function on the elements of the tuple all together.
So it takes the tuple that's captured the parameter pack, and allows you to use it as the parameter pack to any other function again.
Sort of the inverse of `std::make_tuple`.
Right. Thank you!