 Hi, guys. So we just had to talk about a development environment. And what that made me think was we've come to a point where setting up the environment is just as complicated as the coding itself. And I'm sure that's not true, but it's just gotten to the point where the setup, and by the time you start writing your code, you've spent so much time setting up your environment. But this is a nice transition, because this talk is about coding and the language itself. And I'm going to be talking about modern C++. And by modern, I mean 2011 onwards. So I'm sure there's some of you who use C++ on a daily basis over here. But I have about five minutes, like a refresher course, some stuff that I want to go through before we get to the features itself that I want to talk about. And I'm going to be covering the 2011, 2014, and 2017 standards of C++. I'm Nikhil. I work in service delivery as a software maintenance engineer. My daily work does not involve looking at C++ code anymore. But I had about 10 years of experience developing in C and C++, mostly on storage enterprise projects before I joined Red Hat. Modern C++ looks nothing like old C++. Old C++ is generally pre-2011. If you are like me, and you learned C++ in college in the 90s, and then tried to look at something that was written post-2011, chances are even if you know C++ very well, the basics, you may not understand the code nor the way the code flows. There's reasons for that, a whole bunch of new keywords and new types, new ways of doing things, and a highly optimized move mechanism, which I'm going to be speaking about. Here's a brief timeline. I start with 1998. That's because 1998 was the first standard. There was no ISO standard before 1998. The standard was the book written by Bjarne. That was for 15 years. From 1983 to 1998, every compiler writer used to just refer to a book. And that was the standard. We had an ISO committee in 1998 and a major release in 2011, which is going to be most of this talk. We had STL strings, streams come into 1998, move semantics, and lambda functions, which we'll talk about today. The 2014 release was mostly bug fixes and the generalized lambdas. And that's some intense craziness, which I don't want to get into in this talk. We'll refer to it. The 2017 one was, again, a major revision with a lot of features. And we're going to talk about the first few of them in that slide. So since 2011, there's a release every three years. That's the timeline. So there's going to be one in 2022. So let's get on with it. I'm going to have a couple of slides about some basics. So the first thing I want to get into is what is a C++ class? For those of you who may not have used C++ but are aware of an object-oriented way of thinking, a class is a way to create your user-defined types in C++. So I have three classes up here. The one on the left is an empty class. So it's called nothing. And from an external method like main, I can instantiate the class by referring to its type. And just like I do it for primitives like int x, I can say class name x, and that creates the object. The one in the middle is a class which has some primitives. So you build up your user-defined types using the primitives. So int x and int star p. So you have an integer and an integer pointer, a function which operates on the integer x. In C++, you can define the methods of a class in the class itself or externally using scope resolution. But the point is that the methods and the data are tightly coupled and users of the class need not need to understand the internal implementation of the methods of the class. They can just call those methods and they don't have to worry about how it's actually being done in the class. The third one is for, as an example of a class which is a container, because as you can see, it is made up of other classes. When you create a class, you get a couple of things for free. The compiler will give you four, well, six now, but four methods by default. These are put in by the compiler automatically. You don't have to write them. You usually do, but if you don't, you get them for free. That's called constructors, destructors, copy constructors, and the assignment operators. I'm sure you all know about this. The first one is the constructor. The second one is the copy constructor. The third one is the assignment operator. And the fourth one is the destructor. And for people who think that C++ does not have garbage collection, I've heard a lot of debates about Java versus C++ where people say Java has garbage collection. Well, Java has only memory garbage collection and it's not deterministic, which means you're not really sure when the memory is gonna be freed. C++, on the other hand, has destructors, which is a way of deterministic garbage collection, but that's where you write the code to free up the resources which you have used up in your constructor or which you have initialized. Maybe allocated memory, taken some locks, opened some files, opened some streams. So you put in the code in the destructor to free that stuff up. And the most efficient memory reclaim is the close curly brace, one character, because that's when variables go out of scope and when they go out of scope, destructors are automatically called, right? So that's one thing I wanted to cover before I get into it. The other one on this slide is assigning things to each other. So if you have two ints being assigned to each other, bit copy is made, so the bits match. If you have objects being assigned to each other, if the objects are of the same class, again, the bits match. But if you have objects like the one in the middle which has a pointer, those bits match too, which means that you have two objects referring to the same pointer. And that's bad because when one of them goes out of scope, the other one is potentially pointing to freed memory because the one which went out of scope, usually you would have a destructor which frees that up, right? So then your first object is still having a pointer to memory that has been freed by the second object, which is why we need stuff like copy constructors, which is the second line over here. And that is something that does a deep copy instead of the implicit shallow copy that happens when you assign objects to each other. Also you cannot assign objects like the second and the third one, C++ will give you a type error because they are two different types. So making them equal means nothing at all unless you have your own assignment operator where you instruct the language what is to be done when two such objects are assigned to each other. So that was a quick intro about classes and assignments. The other thing that I want to also cover is what does it mean to go out of scope? So we spoke about the closing brace. Ordinary stack variables are called automatic variables or ver called automatic variables in old C++. And why they are known as automatic variables is because when a curly brace is encountered they go out of scope and that memory is released automatically. So stack variables live on the stack just like this character array, just like the character pointer X, just like the integer A. And the class on the left, you have a character array and if you point a local stack variable, a pointer to care called X, and then try to return it, notice that like I told you, the character array is going to go out of scope and you're ending up passing a pointer out of the function pointing to freed memory. The most elementary mistake that C programmers make and this is slightly better, sorry, this is slightly better where you allocate memory and then you return the pointer. In this case, it's going to leak. There's no problem here as long as the caller frees it up after it does whatever it wants to with that memory. The main thing I want to focus on here is that when you return by value like this, when you're returning an integer by value, a copy is made during the return. Copying is expensive. Most of my talk today is about avoiding copies because that's what move semantics is in C++11. But when you return by value like this, if you have a local stack variable, this is fine. You may think that you are still returning a variable that is on the stack and you might ask if it's going to go out of scope, how can I return it? And the answer is a copy is made. The copy is in this integer and primitives which are copied is not so expensive but object copies are expensive. So the whole point of writing efficient code is avoiding object copies. You can't avoid it at times. Sometimes you do want a copy but the compilers can figure out when you are generally passing around temporary objects and it can try to alight the copy and avoid the copy. C++11 also have move operations where you can instruct specifically not to copy. So we spoke about automatic variables which is what these are. These are auto variables. There's also different storage classes like register. Register is deprecated now. It used to be a way of asking the compiler to use a register instead of the RAM to store the data type but it's no longer used. There's also static and external. We're not going to talk about static and external much because that would be the whole talk. So let's not get into that. I wanted to mention auto because auto has changed in C++11. So auto int x which used to be the old C++ way of saying that this is an automatic variable on the stack. The auto now means deduct the type by looking at the initializer. This is done at compile time. C++ is a strongly typed language. So all types are known before the program starts running for efficiency. So the auto keyword is now changed. So if I say auto int, if I say auto int auto i 42 is going to deduct it as an integer. This whatever it returns will be the type of P. Auto is useful for avoiding long types. So I can just say auto iterator. It will figure out the type of the iterator. Very useful to store lambda expressions. And like I said, it's a compile time thing. So type deduction is complicated. This talks about type deduction by Scott Mayers. If you want to check them out on YouTube, long talks about the rules of how auto deducts the types. Initializers are easy but passing auto and returning auto is extremely complicated because this is at compile time. So the compiler needs to generate template functions for each type that you're gonna be actually passing into that auto function. So the only thing I just want to cover as a feature is that since C++ 14 you can return auto from functions. And we don't have that yet but in C++ 20 you can even pass variable into a function by auto. Also a couple of nifty features I want to cover before we get into move semantics. Now you can initialize your class objects using equal to. We could not do this unless variables were static. Sorry. So the thing to note here is that the constructor overwrites the initializer. So if you have initializer as well as constructor the constructor will apply. You can also call member functions here. Even member functions which are defined later on because initializers run last at the last stage of compilation. So if you have a member function which is going to return the value that return value will be assigned to this object in the class. Also we have new ways of writing for loops. We have the range based for which is the same for keyword that we know right from the CDs. It's just gonna iterate over the collection and keep modifying the elements. This for loop goes only in one direction. It's not trivial to write it going the other way. You still use the old way of writing the for loop for that. But it is extremely efficient if you just take a reference to an object in a collection and keep iterating and doing something with that every object in the loop. The other one is a standard library function called for each. And what that takes is a beginning and an end of a collection. So it needs basically those methods to be overloaded for objects. So you can write a for each loop for any object. Your own user defined object. Most standard library objects have those methods already like vectors, lists and stuff. And this is where you define a function to call for each iteration. This is a lambda function. You can also have a function pointer. We'll get into lambda functions later. But you have a function name there and that function will be called for each time the loop iterates. I have one slide for everything else before we get into the only two features we're gonna concentrate on after the slide. Obviously, so C++ 11 and 17 are major releases with hundreds of features. So I'm just touching on a few of them. We have constructed delegations so constructors can call other constructors of the same class. We have initializers in the if and switch conditions. So we don't need to leak scope. So whatever if you're using some variable in this condition, you can define it here itself. It doesn't have to be leaked about and access is restricted to the loop itself. We have a null pointer keyword which replaces the commonly used zero or null that we used to null was a macro. Types can be uniformly initialized now with this curly brace syntax. It works for arrays, objects, primitives. Anything can be initialized like this now. We have smart pointers. So generally modern C++ discourages people from using raw pointers like the one I showed you in my class. They're always to be wrapped around a smart pointer because smart pointers live on the stack as ordinary stack variables and allocations and the freeing up of the memory is done automatically when the variable goes out of scope. So not much chance of memory leaks because all that is managed by the smart pointer which obviously is a class which will contain the actual raw pointer. But when it goes out of scope, that's when the destructor will be called. That's when memory will be free. So if you're using a smart pointer, you don't need to keep track of when did I malloc, when did I free? It's done for you by the wrapper class. There's also static asserts. I don't know if you heard the story about the space probe that crashed because of a runtime error. Static assert throws up errors at compile time. Much better to catch them at compile time. You don't want to have the Mars orbiter run into a buffer overflow at runtime and throw an exception, right? It's no use. Variables can be inlined. There is also guaranteed copy allusion. We will talk about copy allusion. It's very cool. Unions can now contain not just primitives. You can have your own objects in unions. There are a few rules for that, especially about the way those objects are constructed and destructed. But unions need not just have primitives. Like I said, register is gone. So you can't anymore specifically ask for a variable to be stored in a register. Most of the time it wasn't on at any way. So that's gone. And you have constructors which can be explicitly said to default or delete behavior. For example, if you set the copy constructor to delete, it means your object cannot be copied. Any questions about this before we get into move semantics? If you have questions, just interrupt me. You don't have to wait till the end. Right, so this is the biggest design change in C++. The C++11 standard introduced this concept of moving and like I said, we used to have four default functions provided by free for free and now we have six. A move constructor and a move assignment operator has been added to each class. So every object gets move constructors and move assignment operators, which of course you can define yourself just like you could the copy constructor. The whole point of this is working with temporary variables. If you assign a temporary variable into a variable, it has a slightly different behavior because the temporary variable is just about to go out of scope. If the variable that you're assigning from is going to go out of scope, you can optimize because you need not make a copy because you're sure that you don't need to keep that variable around anyway, right? So that's the whole concept of move. A very simple way of thinking about it would be just to think about the shell commands copy and move. So copy, if you copy a file, you have both. If you move a file, the first one is gone. You only have the destination file, right? And to understand move, I'd like to first introduce what is a reference because some of you may know this, but in C++, I can have a reference to a variable and that's just another name for a variable. So over here, I have an integer i and a double d and if I have r, which is an int ref to i, from this point on, r and i are the same thing, all right? Internally, every compiler uses pointers to implement this, but for the programmer, it's not useful to think of r as a pointer. There is no dereference to be done. There are some differences between references and pointers. The whole point of a reference is it's just another name for an already existing variable, which means I have to initialize it. I can't just have a dangling reference. I have to initialize it with something. Once I initialize it, it cannot be changed and for a long time I didn't know why this was like that. Why can't I reseat a reference? But it turns out it leaves the equal to operator ambiguous because once I have a reference tied to an object and then if I assign that reference to something else, what are you saying? Are you saying that reseat the reference and point it now to this object or are you saying copy this into the reference? It's ambiguous, right? So the Bjarne, the designer and creator of C++ just made a decision to just not allow changing the reference once it's pointed to something else. So must be initialized when created and the whole beauty about references is it avoids copies, right? So we spoke about copies, copies are expensive and avoiding copies is what is taught to almost every C++ programmer. Don't pass by value. So the first elegant solution was passing by reference, right? Reference will not make a copy. So if you end up changing the variable you're passing into the function, the variables that you're changing in the function are the actual variables which are changing. So if you pass A and B to swap, inside this function, A and B are being changed and they will be swapped. If you didn't have the reference, copies would be made, copies would be swapped and the original ones would be the same. C++ always passes by value. If you don't use references just like C, some people say that you can pass by pointer but if you pass a pointer, a copy is made of the pointer and both the pointers are pointing to the same object. That's how dereferencing the pointer in the function will change your original object but it's still a copy of the pointer with the same value, with the same address. So that's what a reference is and the other thing I want to also introduce is L values and R values because move is basically everything that move does is with R value references. The references we just saw like this, these are L value references and what an L value is, the best way of thinking about it is whatever is on the left side of an equal to expression. All right, whatever is on the left of an equal to assignment, that's a L value. Whatever is on the right can be L value or an R value. So an R value can never be on the left. You cannot assign anything to an R value. You cannot take its address. It doesn't have an address. It's a temporary, it's going to go out of scope as soon as it encounters the semicolon. Like this is a temporary, A into B. So at runtime, there will be a temporary object created and then that temporary will be copied into this integer. This is a temporary too. You can't take its address. You can't say and 42, right? So there is no address for this. So the whole point of R values is they have no address and C++ lets you distinguish between when a function is called with an L value and when it is called with an R value. The whole beauty of this is that when it is called with an R value, copies need not be made because you don't care about it anyway because R value is going to go out of scope anyway. So why don't we just steal its memory, set its pointer to null. When it goes out of scope, its destructor will be called. The destructor will be called. The pointer will be freed, but we have set it to null. So calling free on a null pointer is a no-op anyway and we've stolen its memory. We've stolen whatever it had allocated. We don't need to allocate again. So we just flip the pointers around. That's the whole point. There is this new reference variable R value reference which is a double AND. The L value one was a single AND. And generally used in this move constructor and move assignment operator. You can do it in any function. So if you call, for example, if you have a function called fun and this is overloaded function C++, you can have the same name taking different types. So this one takes a L value reference. This one takes an R value reference. So if you call it with a L value like X, the L value one will be called. If you call it with a temporary like five, the R value function will be called. So you now get the opportunity to distinguish between if a temporary is being passed to the function or if it's an L value. And the whole beauty of this is you can overload the constructor. So just like your copy constructor, you have a move constructor which will take in a double reference, an R value reference, and the assignment operator which also will be overloaded and takes in an R value reference. And if you see the way a typical move constructor is implemented, the whole point is there is no copy made, there is no allocation done. So I have a my string class which has a data, which is a pointer, care pointer. This is the move constructor. Notice it takes in R value. And what do we do? So data is basically this object's data. This is always passed by, it's implicit. So when we talk about the context of this function, this is in the context of the object we've created for this type. And this is the temporary which we have taken by refref, you call it. And all we're doing is stealing its pointer. So there is no malloc, there is no memcopy. Just point the pointer to the R values pointer and make sure when free is called and the R value goes out of scope, you're not ending up destroying your own. So that's the whole idea about move semantics. Move constructors will be called whenever stuff like this executes. Anytime there's a temporary being passed. There's also standard move which converts an L value to an R value. So if you want to explicitly pass by R value, even though it's an L value variable, you can, standard move will just cast it. Cast an L value to an R value. Similar to this, the assignment operators. So the move assignment operators will do the same thing, steal the data and set the others to null. The only thing is since it's an assignment, our object exists in the first place. It's not construction, it is assignment. And so we need to free our own memory first. That's the only difference. Everything else is the same. This is when the assignment operator will be called. And if you have an overloaded assignment operator with R value reference, this will be as fast as simply flipping the pointer instead of making a copy and then copying the copy. The analogy some people have made is if you want to make a building in a city, old C++ used to first construct the building outside the city and then move it brick by brick to inside the city. So that was what copies was. Copies are expensive. And the whole idea is to avoid the copy. You might think this is so obvious that why did we take so long for C++ to get this? Well, that's because constructors can allied copies anyway. We have something called copy a lesion in C++ where temporaries returned from functions may not be copied in certain conditions and temporaries passed to functions also may not be copied in certain conditions and temporaries assigned may not be copied but under stricter conditions. So for developers, there's not much to do even if you want to use move semantics because the standard library types all have now been rewritten with move constructors and assignment operators. So if you're using a vector, for example, and you fill up a vector with objects of your type or if you fill up a vector with primitives, if they have move constructors, they will be used. If you use any standard types, they will have move constructors. So the whole point is re-compiling old C++ code with new compiler will make it faster probably. So most standard functions, most standard library functions will have overloaded versions for moving. Old code will run faster. The only thing I want to talk about is that some C++ standard library objects like for example vector offer something called exception safety guarantee which means that during the construction or some operation on this object, if something goes wrong, I guarantee you that the object you passed to me before things went wrong is in the same state which means that if I pass a vector to be copied and something goes wrong during the copy and exception is thrown, I handle the exception, the program continues. C++ guarantees that the original vector passed for copy remains in its original state. You can continue off where you left off. Well, moving cannot guarantee that because moving basically means destroying the source object that's being moved from. So you can no longer guarantee that the object is in the same state as it was when the move constructor was called because if an exception is thrown while moving, you may be left with a half of the source object and half of the destination object because half of it has been moved. And in this situation, what does the language do? You could try to move it back, but since exceptions are being thrown when move constructors are being called, moving back might throw an exception too. So in short, there is no way for me to guarantee that if you gave me a vector and if things went wrong, your original data is safe which is why if you don't have no accept for your move constructor and no accept means I will not throw any exceptions. And if a function does throw an exception when you define it as no accept, the program is terminated, it's as drastic as that. If you promise not to throw exceptions, C++ will call the move constructor. If you don't, the old way of copying is still done. We already spoke about standard move. It's just a cast. It doesn't actually move. It just makes an object prepared to be moved or moved from. So that was about move semantics. I have something about copy elision because C++ compilers have been aligning copies for a long time. Any compiler in the late 90s would omit the copy if you return the temporary from a function. So the question is that if copies can be eliminated anyway, if you return values from functions and if the compiler can omit the copy, then why do we need move semantics anyway? It's already much faster than moving because it's not being called at all. So there's a key difference over there. In one case, the constructor is not called at all. So if you have a constructor with side effects, like the simplest way to think about it is a constructor which prints something to the screen. C++ is now allowed to omit that call. So if you have debugging statements in your constructors, the C++ compilers like 2017 onwards has guaranteed copy elision. They will guarantee that the constructor may be skipped and if your observable side effects in constructors may not even have had a chance to run because the call is skipped. Moving on the other hand will be called and you can have observable side effects in move constructors as well. Also assigning cannot be elided. There is no such thing as assignment elision just like there is for copy elision. You cannot elide the copy when you assign. So elision may or may not happen. If it does happen, constructors will not be called at all. If it does not happen and if you have a move constructor, you have optimized code which is going to move and not copy. Any questions about R values, move, anything at all? If you want me to explain it again, I don't mind. So this came into 2011. 2017 has guaranteed copy elision but till 2017, copy elision may not have happened at all. So the other thing I want to introduce is lambda functions. Python programmers will know this stuff, right? So if you write inline functions in Python, you can have a function definition without the need to set up a function call which means allocating a new stack frame, making the call, pushing stuff on the stack and so you might realize that every call, every function call is actually expensive. Setting up a function call is expensive and if you truly want inline functions, there was an inline keyword in C++ but that was not guaranteed. The compiler would not guarantee that that call will be inlined. This is a guaranteed inline. So lambda functions are made into C++ 11. They've been further refined in C++ 14 where you have generalized lambdas but the whole concept is that, I mean it's a little, the syntax is a little strange so it takes a little getting used to but this is basically a lambda expression and it's in place function. The whole thing is basically saying that I haven't, so auto is very useful for lambdas, right? So I have an auto variable. I call it lexp, that's a lambda expression. That's the name. It's just like intx, I've said auto, lxpression, lxp and this is equal to and this is where the lambda starts. So this is going to store this entire lambda. It's an inline function. You can think of it like that and it has strange syntax which you need to get used to. So it always starts off with this capture clause. Then these are the parameters that are going to be passed to the function. You can think about it as a function. So this is the, you call it with passing in, like for example an integer in this case. So you're taking an integer here. Everything in curly brace is what's going to execute. In this case, we're just going to add up these and print them out. So the beauty about this is that as soon as this is created, whatever is the value of x, that's basically going to be what the lambda has. Later on, if you change the value of x, the lambda isn't going to automatically get a new value of x. So the lambda expression gets the copies of the variables that you're capturing at the time of creation of the lambda function. That's the idea I'm trying to get across here. Also notice, I have x in here, which means I'm capturing x. I'm capturing it by value, which means a copy will be made of this integer x. It's not trivial to actually capture variables because this is a whole different scope. The compiler is going to create a class with an overloaded bracket operator for the lambda function. Each lambda function is something like a functor. If people code in Java and they know functors, which are classes overloaded on the bracket operator. So it seems like you're calling a function, but actually creating a new object because the bracket operator is overloaded. You can overload operators in C++ if any of you haven't used C++ before, I probably should have said that earlier, but you can overload operators in C++. So these lambda functions are classes with an overloaded bracket operator. So the reason why C++ is so complex is because something as trivial as f of x can mean several different things. It can just mean a function to which you're passing x. It can also mean an overloaded operator. It can mean many different things. So understanding syntax is the key to understanding lambda functions. So the capture clause has captured x by value. If you put an and there, it captures by reference. If you capture by reference, you can modify this x inside the lambda. Otherwise, you have a copy of x and in fact you can't even modify the copy unless you use mutable keyword. That's because the idea is to generate a point in time functor, right? So if you promise that it's fine, I want to change the value of the copy, then you explicitly have mutable. Otherwise, even if you capture by value, you cannot do anything like x++ inside the body. So in this case, what are we doing? We have a lambda, which is stored in this variable. I can technically define this as a function pointer, like a C-type function pointer, assuming we don't capture anything because if we don't capture anything, well, it's easy for the compiler to create classes which can be cast to a standard C function pointer. Otherwise, the type of a lambda is a standard library lambda type. It's too long to type, so auto makes it very easy. So you just say, okay, I have an auto variable called this. It's a variable storing a lambda. This lambda takes in the upper integer x by value. It takes, whenever I call the lambda, I'm going to pass in an integer to it. What is it going to do? It's going to print this stuff out, which is going to add y. In fact, this is going to add whatever you pass to the lambda to the captured variable x. And this is the closing brace. This is the end of the lambda, and this is where you call it. You can also call lambdas in place by just putting a function call through a function pointer by just putting braces at the end of the lambda. It's just like returning a function pointer. No, so this is a local variable on this stack over here. Yeah, you can, of course, you can return the lambda. Yes, the lambda will be existing if the function terminates. And I know what you're asking here. What you're asking is this is going to go out of scope when the function terminates, right? Yes, the lambda lives. You are working with a copy, so you're fine. This is probably also happening in context of a class, like you have captured this. You usually, a lambda, if you capture a lambda by value, you capture this as well, and you get a copy. Once you get a copy of, for example, once you get a copy of x, and if this is defined global, yes, you can call it in other functions as well. Yes, you can. If I make the reference to it. Yes, because that's just like returning a reference to stack memory. Yeah. Yes. Like you, in C++, you can return the reference as well, but the same rules apply as returning a pointer. If you, like I, like we saw in the first slide, if you return a pointer to memory allocated on a stack, you are returning a pointer, which is pointing to invalid memory. Similarly, if you return a reference from a function, and if the reference is initialized with a local stack variable on that function, then you are returning a reference to invalid memory. All right. Any other questions? Sure. That's, I got, I got that. Yes, so the question is that, why do we explicitly need to tell the compiler that give me permission to change the copy? Yes. It's not, it's something that I tried hard to find out too, because I had the same question. If I pass, if I pass a value into a function, and if I take it by value, a copy is being made, and the compiler has no problems with me changing the copy, because a copy is being made anyway, right? The original is not being changed in that case. The same thing applies here, but the compiler does not let you do that, because the whole idea of a lambda function is to create a consistent point in time copy of, well, every time you call the lambda function with a set of inputs, the same output should be returned. That's the whole point of it. And if my lambda function can mutate without explicitly saying mutable, that restriction no longer applies, because every time I call the lambda function, different things might be doing, might be being done at runtime, depending on some runtime variables, and I may not always get the same standard output back. All right, that's the best I can do with that question. It's a very complicated one, yeah. But the main thing to understand is you cannot change the copy either unless you explicitly say mutable. No, no, no, no, no. Because every time you call the lambda function, you're going to be passing in Y, right? No, it's not Y. Okay, yes. No, it's going to operate with its own copy. No, no, no, no, no, no, no, no, no, no. If you take it by reference, so there's a way to take it by reference. I know, I know that. If you take it by value, you cannot. It doesn't change the next call. Just like calling a function by passing in a value. If you pass in a reference instead of a value, then the original one does get changed. You're no longer acting on a copy. You're changing the actual X. Yes, in this case, you're not. You're not changing the actual X. So the lambda function is going to get that value of X at the time of creation of the lambda. And that's going to stick. So unless you pass by reference, unless you capture by reference, which you can do by putting an and% there, if you capture by reference, then what you're saying applies. No, the mutable is not for what you're thinking it is for. The mutable is to make it explicit that this is not a lambda, which is going to return the same thing if you call it with the same thing. No, because the change that it's doing may be based on a runtime condition. In the lambda, it might be a runtime thing. In this case, it's operating on the values. So in fact, the same thing will be returned, even if you keep incrementing it, because it's going to always work with the initial value of X that it was when it was created. I didn't quite hear the const stuff, what you said. Right, so do you mean making this const, like a const auto lexp? If you make them const, I don't believe you can even, it can't be const and mutable at the same time. So putting mutable in there with a const one, I'm not sure what's going to happen there, because they are two different things. One is guaranteed to change the object and one is guaranteed not to change the object. But the whole idea of mutable is it's not intuitive. It's basically saying that I need permission to even change the copy I'm making of this thing. Capturing variables can be like I already referred to in the earlier slide. You can capture by reference and value. If you capture everything by reference like this, then you can refer to any variable above the scope of the lambda in the function and actually change its value. So the next time the lambda is then called, there will be a change in the value. If you capture by value, you're always working with copies and those can't be changed unless, like I said, you use mutable. If you use mutable, you're changing the copy of the lambda in that particular scope, but you're not changing the original still. An empty capture clause captures nothing. And you can capture certain by value, certain ones by reference. Everything by reference, everything by value. You can capture this. This is implicitly captured any way when you capture everything by value. By the way, this is a pointer. So you're capturing the pointer. So you get access to member variables if you capture this. 2020 or 2017, I'm not sure which one of them, allows you to capture star this, which means you now end up with a copy of the variables of the class, which means that if the lambda is referred to at a time when the object is going out of scope, it still works because now you're working with a copy because you've captured star this. Till 2014, C++, you could only capture this by value, which means if you're going to be referring to objects of the class, you better hope they have not gone out of scope because you're working with pointers to those objects. Same concept as what you asked. So if you really think about it, the reason why that question is so complicated, why do we need to explicitly say mutable and even then why do I need permission to change the copy? That's because like he said, it's a point in time closure. How this happens is every time you write a lambda, a new class is generated by the compiler for it. The class will have an overloaded operator and captured variables become data members of the function objects. So they are class variables of that class. This is some differences between the three versions of C++ I've been talking about. Like I said, C++ 17 allows to capture star this. And the last slide I have is GCC support for C++. So if you want to use the new versions of C++, you need to pass them in to the standard, to the STD argument to the compiler. I think it's default, GCC 6.1 and above. This is passed by default as C++ 14. But if you have some old legacy C++ code, you might need to add this to the compiler if it's an older compiler. And you might see your older code run faster with everything that we've talked about like moves and stuff. The GCC I've seen on rel 7.5 is 4.9 point something. Yes, yes. So it does have C++ 11, everything. C++ 14 is a default in 6.1, but everything after five has 14. And that's it. That's what I have unless there are more questions. One question. So the question is how safe is moving? And it's not safe because, it's not safe in the sense if it's sure, if it's definitely a temporary that's being moved on, it is safe because the compiler knows that you're not gonna do anything with that temporary anyway. For example, old C++ also let you take a reference to a temporary object as long as you called it a const reference. As long as you promised I'm not gonna change it because changing it means accessing it by address and it doesn't have an address. If you just wanna print it out, sure, do what you want, right? So const references was there and see old C++. As for whether it's safe, well, if I standard move an L value, and force the compiler to treat it as an R value, nothing stops people from using that moved from original L value. So it's not safe in that sense because the original object is destroyed, the pointer has been set to null. So in that sense, it's not safe. You have to know what you're doing. They say that C++, C, you do what you want and if you shoot yourself, it's your fault. C++ makes it a little harder to shoot yourself but if you do end up shooting yourself, you blew away your whole foot. So that's something about probably very applicable to moves because you can end up destroying objects which may be referred to later if you've done an explicit move from them. Nothing stops people from trying to refer to those objects if they were original L values. All right, thank you, guys. Thank you.