Back to Basics: Move Semantics - Nicolai Josuttis - CppCon 2021

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
whose life would be better if you knew more c plus whose life would be better if your co-workers knew more c-plus plus let's get started welcome everybody um this is one of the talks about um back to basics so that means we could talk about the basics of c plus which are a lot which are sometimes a little bit complicated and this time i want to talk and discuss with you move semantics my name is nico yosutes um you might have heard my name um yeah i am in the superstar standard committee for more than 20 years now so it's partially all my fault and then i also wrote some books about it that's it for the moment so to understand move semantics um let's look at a very simple example just four statements here look at this on the um left we have four statements and on the right we look what happens on the stack on the heap with the memory while the program is running performing these statements so at first we create a vector of strings not initializing it with any members just empty thus having zero elements and then we allocate some memory we reserve memory for three elements or for at least three elements so this memory will be reserved by the vector on the heap but first reserved there are no elements yet so still we have zero elements inside and then we have we create we initialize the string s with the return value of calling getdata assume getdata returns a string data backslash 0 at the end and then we push it the string into the vector so now things become interesting um our containers have copy semantics have values amendments so that means whenever you pass an object to insert it into a container um the container creates a couple and that's what you see here create a copy a full copy a deep copy of the string passed to the container so we have now a copy including its own memory on the heap for the value of the string for those who have heard it yes there's a short string optimization so for very short strings springs might not need heat memory but let's assume this is a longer string of a principle in general so in principle strings need memory and if you create a copy you create a copy including copying the memory allocating memory and then copying the characters to this new memory so the vector now has one element with four characters stored in here in this memory so we could do the same um like the previous two statements um with by just passing the return value of getdata to pushback so in that case now things become interesting because before we had move semantics and that was in the old world of c plus plus so with c plus plus 98 and c plus plus o3 then the following did happen we created a temporary return value the return value of get data assume now we this function returns data too and then we pass it to pushback and as usual pushback creates a copy and then this is not the end of the statement the statement is not done yet we have a second element in the collection we have the return value which we used to create a copy from but at the end of this statement we destroy all temporary objects so that temporary object will be destroyed and that's not good because um we have uh we had a return value allocating memory we allocated new memory copied the data to there and then we freed or destroyed the old memory and memory operations on the heap are pretty expensive so that is something we wanted to improve as an example and let's look how this works um let's take the same code as you see here we are still at the situation that we have called get data and we are about to pass it to coachback but now we have recompiled our c plus program with the c bus 11 compiler because this compiler supports move semantics and what move semantics means here is that the compiler does not create a copy of the past argument the temporary object the temporary string it uh well moves the data from the temporary string to the new element so the move is implemented in a way that we copy the address of the memory and we copy the five so to some extent this is a shallow copy just copying the members but that's not enough it's it's that's not enough we move the data we don't copy it or now use the same memory because we we want to make sure that the original return value no longer is the owner of this memory so part of pushback is another call which is let's remove the data from the return value the temporary object that was returned by getdata so that is a clue that is that is how things work and this happens automatically please note that we still have a temporary object and still at the end of this statement this temporary object is destroyed um as usual at the end of um statements temporary objects are destroyed so we now can call the distractor and the distractor will not free any memory because the memory is gone and was moved to well the new second element of the vector and that's the whole basic magic of move semantics we just recompiled the program and by some rules in the language by some implementations in the in the associated classes and types like strings and vectors magically this code is significantly faster because instead of copying the data and then deleting the old data we move the data from the return value to the second element before we destroy the return value and that means we are significantly faster so if we don't use s after here the second statement anymore we would have you think the same effect but for that the compiler would have to find out that this object is no longer in use which the compiler could but it would take a lot of time so and you have to double check for side effects is a reference to s or so so here in this case the compiler knows that the return value will be destroyed with the end of this statement so we we can use code that safely steals the memory moves the memory from one place to the other and please also note that move semantic does not mean we move an object from the stack to the heap now here are some bytes and we have to copy the bytes but these vibes refer to some memory and this memory we can now reuse somewhere else so we now point and own the memory that was formally returned by getdata as a first lesson you see something and you see the following rule in doubt deciding between these two lines and this one lines the letter is better so avoid objects with names that's the first lesson guarantee so you cannot always avoid objects with names well first of all this is not the only rule we have yeah if you if you try to write a c plus bus program without any name that will be fun um and it will probably not readable at all not be readable at all so this is only one rule that says in doubt avoid objects with names and beside that they're sometimes it's less readable less maintainable there are situations where you can't avoid having objects with names so look at this if getdata returns a value and you want to insert it into two different containers you need the return value twice and to use it twice you have to give it a name and when you have a name then by default copying is used move semantics will not be used here even if sdr is not used afterwards another example is if you have parameters so if your function takes a parameter s um and you might move it into some history buffer before you re-initialize it with some default value um here you no longer need it and you could move the memory into the history buffer but as it has a name and as a copy as a compiler does not know whether we still need this object well in fact we need it again um then the compiler will say oh i perform a copy and this is the third example if we read line by line with get line from a stream we read into a string and then we push the data into a string again we could move the data into a string because we know no longer need this memory and the value and its memory anymore before we read in other data into the string for all these cases we have something called roof std move is the statement to say oh let's switch to move semantics although usually we would not use move semantics here and please know that move does not move move says semantically i no longer need this value here and as a consequence the code moves switches to move semantics from coffee semantics whether it's here or there or there so let's look at this into in detail so coming back again with this example we have a vector we have a string and we want to push back the string into the vector yes s has a name this is what we call an l value an object with a name this object will be copied into the vector and if we would call pushback with s again we would copy again so that we would have three different strings each swing has its own memory its own value but it's okay because if we wanna modify the first element in vector the second and s should not be affected but here you can say i no longer need s anymore how do you say that you mark it with move once again this mark doesn't do anything at all with s doesn't modify it it doesn't move it anywhere it's just a mark a stick oh in fact you change the type a little bit maybe maybe not and what you say with move is i no longer need the value of s here and with this information when we pass s with this information to push back then we switch to what we have seen so in this case we also switch to move semantics which means the vector the new element in the vector gets the address of the memory and we remove the ownership of this memory from the string here in s both hands let's move them in we have moved the value from s to the second element of the vector this time we had to program something because we had an object that had a name and if an object has a name things don't happen automatically we have to say we no longer need the value here now it's an interesting question what is the state of s afterwards is s half destroyed is it a valid string is it completely destroyed what is it can we still use s well what we specify in the c bus standard is that an object where you have performed a move is in a valid but unspecified state please note that this requires corresponding implementation which we can only guarantee for library objects for your types if you program types that allocate memory it's your task to ensure that this is also the situation highly recommended so for library objects and vectors strings etc are library objects we are the string s after this call is in a valid but unspecified state and that means you can deal with it like with any vector or where you don't know the value so think about you're in a function and you have a parameter s being a string it's the same situation you get a string you know that it is a valid string but you have no clue about its value so you could say let's print it out let's compute the size deal with the size yeah that's fine that's perfect legal the only thing you don't know is what is i afterwards and what is printed out but what we definitely know is if the string has four characters these four characters are printed here and i is four so we are in a consistent state um by the way it's pretty likely that the string is empty but it's not guaranteed because we all do this for performance um so it's probably an empty string but that's not guaranteed in general so you can even append something you don't know what is in front now of this dot in your string probably no character at all maybe some and you could uh then use the first character which might be this dot or what we had before and you should definitely not use the sixth character because there might be none if you do that you have undefined behavior at one time or might have it and and that's a very useful thing you might say okay let's assign to ask a new value so this makes it clear as is not broken as is not destroyed as is not half destroyed you can still use it now a lot of people wonder why this makes sense why don't we say after a move the object is after move the object is still usable in a valid but unspecified state here is the example let's declare a string let's read in some data into the string from the from an io stream which might be a file or whatsoever so let's read the next line the next row from a file it into this string that's what getline does it returns whether there was still something red and now we have a string with memory and this string we can move now into the vector because yeah we have a string with associated memory this string we want to store in in a collection of all rows of the file so there we need the memory good so move it to there but afterwards we read again and when we read again we still use the string and allocate new memory with new for new value etc so this is a this is an example where after moving away the value we reuse the string and by the way we don't reuse it by just assigning something we call getline which stores a new value here's another example swap for two strings a takes a string well let's move the value to a temporary value and then let's assign the value of b to a it's also we have moved away the value so that means the memory was moved to 10 and then let's move the value of b to a move assign the value of b to a that is also using an object after it was moved so that's why we um why we make why we have the rule a moved object is still a valid object when this website dies it will then free the memory the new object is the owner of the member so let's look a little bit inside so what we had the situation a situation before we introduced move semantics was this we had a class vector with pushback taking an object of type t t is the element type it's a template and then whenever we call pushback we call this function and this function took the parameter and created an internal copy so independent from what we passed whether we had an object which we still need or temporary object like here or here oh and we move s again and push back s again and we don't need it anymore since c plus plus 11 uh but these are unnecessary couples so since c plus plus 11 we have move semantics and that means we have a new pushback here an overloaded pushback this is a pushback here still there for objects the cop where the caller still needs the value this is pushback where the caller no longer needs the value um this is the way to declare it's called what we have say our value reference this is call by reference so alan refers to whatever we pass whatever we do with alem we do with the object we pass but with these two ampersands we give it a different meaning to ampersand signal um it's okay to steal the value and it will be automatically called when we pass temporary object whether it's the return value of a function or maybe the result of an operation like s s and if we mark an object with move saying we no longer need this value in all these three cases we call this pushback if we wouldn't have it we would still call this this is has higher priority than this in case somebody has this pushback and we pass a temporary object or an object marked with move and we have declared it with two ampersand no cons because we modify the object we steal the memory then we have here the pushback that will be called and performing the stuff we need now vectors don't implement string operations vectors just say oh oh you gave me something you still need i shouldn't modify it or you gave me something where you no longer need the value what the pushback calls is a helper function of the type and you might have heard about this helper functions one of them is called the copy constructor so as in our example the elements are strings let's look at a simple string class implementation where we say let's create a string from another string where we still need the value that's what we call a copy constructor and that copy constructor uses an l value reference const l value reference so a classical reference if you feel better but we have now a new implement function for this string and in this string type we have a function taking a string where we no longer need the value constructor means let's create a string from this argument which means we no longer need the value so this is the move constructor it uses r value references so look at our example if we have a string x initialize with move the string might might look like this and now we call pushback for x so we push back x into the vector this is like let's create a new element e1 i've called it and initializes with x and that is calling the copy construct because we have not said we no longer need x so the thing on the left will be called and the thing on the left will according to its implementation copy the length allocate memory here and copy the characters so we have a copy of this x now as an element into our vector if we call pushback with move of x so we say we we no longer need the value of x we switch to move semantics so the compiler says oh then i can go through my type i type as a string and call the move constructor and this is like initializing a new string with a existing string marked with move oh this should be a y anyway so so here's the implementation on the right so instead of copying only the length we copy the length and the data now so that means the pointer to the memory we copy this is now we share the memory from the existing object and now um we steal the memory from here so i should say this is the move of e yeah so this picture is not exactly right good so that's the idea and that way we have the effect we want so that's the heart of move semantics again please note move does not move both only marks for move and in fact it's equivalent to a static cast to a string are there a reference you could even write that instead of marking your object with move would have the same effect yeah so that answers the second question that is in the q a yes so that means since move semantics we have different ways to pass by reference you could call a function to take a const l value reference and you could call a function take a non-const l value reference that was what we initially had and the one thing was to read something read from a parameter avoiding to copy the parameter and now here we write we can modify a parameter this is what we could call an in out on the out parameter now the third thing we have gotten is this um we can call foo with other your friends that has move semantics so that means whatever you give me i can steal the value i can adopt the value this compiles only for objects without the name or mark with move this is what we call our values interestingly there is a third option a fourth option you can say let's mark the object with two ampersand so move semantics and const that's possible yes that's possible that will compile but that makes not a lot of sense because that's a semantic contradiction because this is for the case when the caller no longer needs a value but we are not allowed to modify the values so to steal something okay doesn't make much sense but surprisingly you can get it pretty easy initialize a con string and mark it with move and you have something something like that and that's interesting it's this is not an error this is this is valid it's just that the move semantics is not implemented we have no pushback for that and we could not implemented that it steals the value so we're using the function above here and say okay this is the function we call so the move has no effect it's not an error it is not an error because in generic code it could make sense um to mark things with move in case the type is cons it will should be ignored and if it's not cons it would not be ignored so we in that case we want to use move if supported otherwise we would copy yeah and the same is true if you try move on a parameter declared with cons and please also note that a function should never return something with const value because if you then take the return value and pass it to push back you have disabled move semantics code still compiles again but it will come are there any questions so far yeah i see what does a string e equals x called copy assignment oh yeah it's a it's an it's a usual misunderstanding that people think this is the assignment operator this is only the assignment operator if the object exists already as we declare a new object this is a way of initialization and that is calling a constructor always this is not an assignment never next question could we use move to implement zero copy technique yes in fact you could because you could say for example you could even disable copying at all we see that in a moment in the next slides so you could only enable move and that would mean if your object represents a resource you can only move this resource around but you could never copy it and that by the way we do in a couple of types that's move only types yes that makes sense and that's pretty easy to program just disable copying and enabling moving what happened when the scope of the move variant is it's still available in the outer scope well um the moved value is no longer owned by the initial owner the move value well its memory was moved to the new object that is the owner so at the end of the lifetime of the of the new object it will be destroyed automatically okay maybe i'll skip to the next questions afterwards and continue it's a lot to say about move semantics you have seen the basics the basics like pretty fast so let's let's look into classes let's look how we um deal with uh move semantics in classes in our own classes so we have the following guarantees um unless otherwise specified moved for objects are in a valid but unspecified state rule one that applies to objects in the library then please note copying is always a fallback you have seen it you you saw that um we might mark objects with move if there is no support for move semantics or if it's not possible because it's cons as a fallback um move semantics copy semantics is used you can disable this fallback i just told you that then you have move only types for example thread objects streams and unique pointer are examples for move only types so you can't copy a thread you have no idea what that means we have two threads then or what but you can move around a thread by so for example the object can return it to the caller and using move semantics the next thing is default move operations are generated um automatically so there's a move constructor and move assignment operator usually automatically generated and it moves the members so your old type has move semantics similar as your type has copy semantics even if you don't implement copying just by moving the members but only if this can't be a problem and it could be a problem if we have something special program with copying destroying ourselves so we only generate move operations if there's no special member function declared if there's no copy constructor assignment operator or destructor so what does that mean look at this class we have a class custom this customer has members two of them are strings um the usual constructor but we have no copy constructor no moves constructor i should also write you know destructor defined declared so in that case move semantics is automatically enabled and unless it's not implementable so if one of your members you can't we can't move a copy then it's not possible to do that but in general it is and that means the following happens if you create an object of this type first name joe last name fox and then we say oh move this customer into your vector this will have move semantics and the move constructor that is automatically generated will delegate move semantics to the members that says it will move the first name and the last name of c1 into the new element in the vector and that means if you then afterwards use the object which is allowed remember we still have a first name and the last name but we don't know about its state it's probably empty i'm not guaranteed so when we print it out we will see that the first name and the last name was moved away well i wrote here three question marks it's probably it's very likely that it's empty but that's not guaranteed at all if you instead declare something declare us enough not implement declare something is enough for example say um please generate the copy constructor with its default behavior then you have disabled move semantics because you have said something special about copying and if that is the case we thought maybe it's not a good idea that we implement move semantics automatically there was a reason you did not use the default generated copying so you probably would also have a reason to do something with move semantics this might be type you have programmed before c bus bars 11 if this is just implemented and not defaulted and it should not it should still work so yes having declared one of the other special member functions you have disabled booth semantics but you can still use move because you can create an object you can move it into the vector but as i told you we have the fallback mechanism by default if we can't use move copying is used so this will copy the customer into the vector and that means if you print out the customer afterwards that was where the value seemed to be moved away it's still there you can implement it yourself as you can implement copy constructors you can implement move constructors so it looks like that so this would be a copy constructor create a new customer from an existing customer see well initialize the first name with the first name of c last name with the last name of c the value with the value of c that would be a common copy constructor and for the move constructor no cons to ampersand let's initialize first with the first name of c but we no longer need the first name that's interesting you have to specify here again that we have move semantics it's pretty interesting if you declare c with two ampersand and you use it it does not have move semantics if you declare it with two ampersands then the caller can give you this object and declare he or she no longer needs this value so the caller has said i no longer need the value so we can say by ourselves when we no longer need the value because remember we could use it twice we could use this parameter twice and if we immediately would move it away with the first use we couldn't use it a second time so we have to say now oh and the first name we no longer need move it to the first name of the new customer the last name move it to the first name of the la to the last name of the cust new customer value also you could by the way also move the value but as it is an end there's nothing to optimize with move semantics ins are completely on the stack um that would have no effect but it would compile but we do something else we negate the value just to signal the value was moved away yeah smarter things to do just for debugging also but just for fun let's do it that way and that would mean um customer initialize and we move it into the vector so the value is moved away now first name and last name i moved away and the id the value here the end value was negated so we would see it that way please note that for some reasons we have no time to talk about if you implement the move constructor yourself it should ideally be marked that it does not throw any exception there are good reasons for this well unless it might throw which pretty often is not the case so it's it's not pretty often that you have to implement the move constructor yourself but therefore this can be pretty complicated but that goes definitely beyond the hour we have here and let me finish with one more example because what you just learned has an impact on inheritance so if you have a virtual disrupt destructor you also have disabled move semantics you have a special member function and special member functions disable roof semantics so that means if you have a class maybe a base class a polymorphic base class and you have declared a virtual distractor as you should do in a polymorphic base class you have disabled move semantics not for the object as a whole only for all the members of person so if you have no members there which is pretty often the case don't worry no problem but if you have a member there which could benefit from move semantics then you might think about to declare move operations to support move semantics for this member yeah a couple of things i'm missing here base classes should have a protected assignment operator etc but just as a general rule about move semantics here in the derived class so you have a customer here derived from person so a customer is a person with some additional attributes we might here now implement print here we only have declared it as an api a throttle function um it's optional whether you write vertical here not therefore it's light red and please some people do that don't do what you see here don't do that no never do that you are implementing a destructor don't declare again a virtual destructor because this disables move semantics for all the members introduced here so if you have a vector of data you have disabled move semantics when a customer gets moved instead of moving the data from one vector to the other we would copy the data and that is expensive because vectors use internal memory and it would be a nightmare if your vector would be a vector of strings and each string will also allocate memory then it would be a nightmare to have that so don't do that if you have that some people have that because it there was no problem with that in the past before we introduced move semantics please remove it unless you have to implement unless you have to clean up something in the destructor of course then you need that okay that was the second part of the talk let's see so a user-defined type with cons members is not movable right you're right but it's also not copyable by the way and uh well uh copy assignment does not work and a copy constructor would work is the move std move thread safe or no no no move move is just an operation like copying it's independent from thread safety um if you move some data from one object to the other and these objects are used in different threads you have to take care of the problems it's think about and optimize copying with all the problems that have to be solved if you implement copying in a multi-threaded environment okay that was yeah were these two questions so last topic last topic and this one how perfect forwarding now it gets ugly now it gets ugly because there's an issue there's something we didn't design very well in the c plus 4 standard committee yes we make mistakes but we are in good shape with semantics but here is something to improve and you will see what i mean so think about you have a class c and then we have three functions overloading one function with three parameters one is in case you give me something to read from of this type a const reference so read only excess one ah right access so this would be an n or in an out or an in-out parameter and one with move semantics so here we implement if the caller said um i no longer need the value for some reason different behavior is provided here there are a lot of examples okay so as you have learned if i have an object of this type this will call the second one this is a non-const object so it will call the function with right access we have an object with name so we don't use call move access we have a we have a modifiable object mutable object so we don't call the function with const if we have a const object the first function would be called that's the only function that is allowed to be called for objects that are declared as cons because we guarantee we will not modify the value and if we have a temporary object or an object marked with move the last one will be called because remember a temporary object just created here on the fly or marked with move means the caller no longer needs a value so this will be called now think about we are not calling directly foo we are calling foo indirectly function call and we want to call foo the right foo so we want to make sure that this behaves the same as if we would call foo directly this is what we call perfect forwarding kofu should take the argument and pass it forward so that the right foo is called perfectly forward what we got to fool so we would have to implement three functions called foo here but the interesting part is according to a rule you have seen before only the first two would just pass the argument the last one would need a move because do you remember that you have seen that when we have implemented the move constructor if you declare here an object with move semantics the object itself has lost the movement it's only said it only says the caller can give me tell me that way the caller no longer needs a value we have to say again that we don't no longer need the value so we have to forward it calling with move again passive calling move again marking it with move again here move would be a problem would call the wrong function here not calling move would also call the run fund and that would mean we would have three functions to implement for one argument and if this is generic code if we have two arguments we would need all nine combinations with three arguments we would need 27 com combinations all 27 combinations of maybe the first argument is constant maybe the second has moved semantics and so on that would be a nightmare so we came up with special rules in c plus plus and this special route is called is called perfect forwarding perfect forwarding is something special it looks like something you have seen before in this talk but it is something special and for perfect forwarding you need three things three things all of them you need first you need the template parameter without the template parameter you can't have perfect forwarding second you need a parameter in your function of the template parameter type declared with two ampersand this looks like the rule of two ampersands apply but because it is a template parameter with two ampersand different rules apply in this for this combination we have different roots we call this a universal or forwarding reference yes we have two terms for that in the community unfortunately um both terms are used and then inside you forward it not move it forward and the and and the magic of the rules in the in this uh code is that by rule if you have these three things this forward becomes only a move if you passed an r value if you pass the temporary object on object mark with move so if you pass an l value an object with a name it will it will just pass x forward without marking it with move again and in the other case it will forward it with move okay one explanation here the term universal or forwarding references we have two terms and the community invented the term universal reference because this is a reference that can refer to everything while the c password standard committee decided to say we want to call it forwarding because usually we use it with forward which by the way is not correct um so i think it was a mistake to call it forwarding reference both terms we have now so look at the difference if you see in your c plus plus code two ampersands behind a name you have to check very carefully what it is is it a type name then it's a raw r value reference you only support move semantics and that means you are not allowed to pass an object by name you can only pass temporary objects or objects marked with move only that will compile if you see two ampersands in your code and these two ampersands are behind a template parameter a local template parameter not somewhere else then this is a universal or forwarding reference then it can take everything and then a few things other things are different so for example here we are we can be sure that this type the type of x is not const here it could be cons here to forward it as we know this could only be something that has move semantics from the calling side we can forward it with move here we have to use forward because we should only forward it in this in the last two situations when it was a temporary object or an object marked with okay that was a tricky part yes we should have had a different syntax for that than this that would have been better looking back we made a mistake here yes and terminology universal reference and forwarding reference is also not something we should be proud about so yeah that happens okay so let's uh summarize and look finally at a few examples um here's the first example we have our customer the vector of customer we push back a temporary object so we create here in the yellow box we create a customer so which means we first have to create two strings and then we take these two strings to initialize the first name the last name and 42 goes to the value of the object here we have the yellow box we have created a temporary customer as move semantics is automatically generated if you then pass this temporary object to pushback this will be moved and that means the memory of the first and the last name of this temporary object in the yellow box will be moved to here so we avoid an unnecessary memory allocation by the way there are even tricks to make sure that the object f and l here is moved to here that goes beyond this hour um i will describe it in my book about move semantics you have to do something over there you might have heard about a function called and placeback so look at that if you have a string initialized here that's a string jill and you have a last name cook so this is first name this is the last name and then you say oh please um create a new element in the vector and for a place back you don't pass customer you only pass everything you need to create a customer but now you say i no longer need the first name but i still need the last name how does c plus plus support that so the first name should be move the last name would be copy that should be the effect and that works because the emplace back function has what you just have seen it has universal or forwarding references so template parameter two ampersand and forward these three things that you find here but just for an arbitrary number of parameters therefore here are three dots here are three dots here are three dots so here you see applied perfect forwarding in this function and placed back to support move semantics final issue there's one more thing to learn so look at this you call a function compute you get something bad and what you get back you pass to process we also want to have perfect forwarding view if compute returns a temporary string and we pass it to process process could take the string and steal the value because the return value of compute would die if it's cons this should not be the case so yeah move semantics should also work here and it does it does all is fine unless you want to do something between calling compute and process with the result so if you have to do something like that compute and then later process it you need something like this this is a universal reference so like the template parameter with two ampersand but it follows the same rules this is a temporary object or not depending on what it has it can refer to everything and if what we got back here has move semantics we forward it to process and this forward becomes a move that is what we call a universal reference that is not a template parameter that was also invented with move symmetric and it's called its alto r value references the funny thing is that these are value references are more and more important in c plus um so in general we can refer to something by saying it's a const l value references then whatever we refer to we can only read it's const what we refer to or we can have non-const l value reference if we have that uh you we cannot refer to a temporary object by rule so a non-cons l value reference cannot refer to everything but as you learn two ampersands in the template parameter and the same applies to two ampersands in to auto because both are universal or forwarding references can refer to everything and keep constants and cons non-constants alive and that is a side effect of c plus plus 11 and move semantics which becomes more and more important now in c plus in fact in c plus plus 20 you will use that a lot that's my final sign because in c plus 20 you can do the following you can say here let's let's implement a function print taking an object where we can iterate over a collection by const reference well this looks like we should only read so cons alvarez reference looks perfectly fine and it works if you take a vector and you call print with this vector we're iterating over the elements of this vector in c plus was 20 we introduced views so you can even pipe the vector into a filter like drop and say oh please drop the first three elements so which means print prints um all elements starting with the fourth element skipping the first three and that compiles great however some views with some views code like this does not work for example if you apply a filter filter saying i'm only going to have these and that elements all the even or all the other elements also why the hell is that the case because we have said what we got is cons and some views that's what we call as a view into a collection do not support iterating when there are cons if you take a list then even drop does not work so it's even so that for some viewers in general not possible for some view it depends where it refers to that's one of the flaws we have in c plus 20. why do i tell you that because the solution is a universal or forwarding reference because remember this is a reference that can refer to everything without making it cost so it can refer to a vector it can refer to all the views so all these errors would no longer be an error so in generic code um you can program it now that way and you see you can write it now in two ways template parameter two ampersand off out to two ampersand which is also now possible for ordinary functions in c plus plus twenty so therefore move semantics was invented with c plus plus 11 but more and more features of c plus plus 11 especially universal on forwarding references become more and more important in c plus plus now with c plus 20 especially and that's it i have written a book about that this book has 240 pages we have made i don't know half of them and um yes very fast usually i take i don't know half a day or even a full day with all the details um if you want to read more look at that it's tricky i know what i didn't tell you but you got the basics you got the big idea maybe also not to complain about that anymore so long and yes there's also a c plus 20 book coming which is currently already out as a draft with automatic updates when there's updates so if you like and as you think it's worth it um you might think find new more informations about that in that book so let's see um are there a few discussions left we can immediately switch to a gather time but let me just finish the last questions here why not use struct without any constructor well um maybe that was yeah yeah my examples were simplified i just had members yes you could also use structs the same rules apply structs also have more semantics i just skip other things for if i have a class of course i can i have more control about the values think about i want to ensure that the last name never is empty you can't ensure that if you have a strike but all i said here does not behave different for structs and for classes do you need to set first and last to empty no well remember in my in my examples if you if the move semantic is automatically generated for the customer they will have an un unclear value so but that's okay remember either it's a temporary object that immediately will get destroyed so it doesn't matter to which value you set it in your move constructor or you have um an object marked with move and that means the caller no longer needs the value so yeah it the caller should no longer care what the value is if it is important for the inner consistency of the object that after a move your object is still consistent so for example if the string is not empty then you have to implement move semantics because by default a move from string is m is probably empty so in that case you have to bet you better implement both thematics yourself yes and that would be one reason to do so i also cover it in that book so that's it i hope you enjoy um tomorrow i talk a little bit about about numbers um and you can meet me and gather town now on the on the on the panel on the podium of this track and give me two or three seconds to withdraw to go to there and um see you and have good fun at cppcon hopefully next year i'm allowed to fly in and we see personally thanks goodbye you
Info
Channel: CppCon
Views: 3,254
Rating: undefined out of 5
Keywords: c++ talk, c++ talk video, cpp talk, cpp talk video, c++, cpp, cppcon, c++con, cpp con, c++ con, c++ tutorial, c++ workshop, learn cpp, learn c++, programming, coding, software, software development, cppcon 2021, move semantics c++, what is move semantics in c++, how to use move semantics, rules of move semantics, copy semantics, std move c++, vectors move semantics, c++ move semantics, special member functions, perfect forwarding c++, nicolai josuttis, nicolai josuttis c++
Id: Bt3zcJZIalk
Channel Id: undefined
Length: 63min 57sec (3837 seconds)
Published: Sun Dec 19 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.