CppCon 2019: Klaus Iglberger “Back to Basics: Move Semantics (part 1 of 2)”

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so welcome ladies and gentlemen welcome to back-to-basics move semantics part 1 I'm classy Chewbacca you can consider me your guide into the realm of movement move semantics so I kind of expect there's two groups of people the first group of people as the people who potentially do not do not know move semantics yet or still struggle with the details this talk is exactly for you so you were very welcome but it's also another group of people probably this is the guys that already know about move semantics potentially even know more than I do okay you of course welcome to but I have requests so I have a couple of interactive questions at least give the other group three seconds to think about the question and then give it away is it okay for you so now if you count the three in your head and then said yes we have a deal alright perfect so the short introduction of myself so as I said I'm Klaus I have been a c-plus Serena since 2016 additionally I'm a senior software engineer at Siemens had also know one of my work I'm the author of the play C++ math library and also one of the four organizers in the of the Munich C++ user group which by the way is truly an amazing group if you happen to be in Munich any time check it out fascinating group I'm also record a percentage C plus because conferences so this is a talk in two parts in the first part we are focusing on the deep on the on the basics we talk about the basics of move semantics the new special member functions and parameter conventions later in the second part we dive into the details this is where it gets purpose a little gory all right I have a couple of thank-yous I want to thank Scott Myers Nicolai Hugh suitors this is the two guys I learned move semantics from so if you have the opportunity check out the teach material this is really great and I also want to thank how I didn't is kind of the mental father of move semantics so if it wouldn't be up if you wouldn't be then I wouldn't have anything to tell you today all right let's first of all motivate this let's go to the real basics what are we about to do let's imagine that we have a vector of integers quite simple this vector would be represented like this the vector itself the static part is just three pointers to int there's a couple of addresses of course and then there's the dynamic parts 1 2 3 4 5 this is somewhere on the hiepro free store now I have a second vector v2 it's empty at this point n is so at this point it is just a yeah just the stack part all the addresses have been zeroed out all the pointers now I assign v1 to v2 now what do we expect to happen Oh question all right the question is what are the second two pointers so by the way if you have a question please any time but prefer preferably use the microphone it's much easier much better for the recording and of course then I don't have to repeat the question the second two pointers so the first point of points to the very first element this is the beginning pointer the second pointer basically represents end and the third pointer in this case exactly the same thing would represent the very end of the memory that you have this is something you could use to compute a capacity yeah all right all of them are zero at this point now I assign v1 to v2 what we want to happen this case is something called a deep copy we want to have an exact replica of v1 and this is absolutely reasonable because this makes our code easy to understand easy to comprehend we take a look at this and we know that afterwards we do not have to take care of any kind of shearing so we want to have a real copy a deep copy despite my call to move value semantics let's do another example let's say we have a function create vector that is returning a vector and again I have an empty vector v2 v2 again is just an empty vector zero dog pointers inside let's assume we directly assigned create vector to V now the first thing that happens is that of course I return a vector from the function that function is well that that value is something I don't really have a name for I call it temp and it is created by something that we can ignore the return value optimisation now it's somewhere in the calling scope of this of this function call now this one is assigned to beat you but do we really want to create a deep copy at this point oh this would be a shame because of course if you do a deep copy temp will be destroyed at the end of the statement and we have wasted a lot of energy that is not what we want to do what we would really want to do is actually we would like to transfer the content of Tim to v2 in a very simple way we simply want to well first of all copy the pointers that's the first step we cannot stop there at however because at this point there would be two vectors owning the same memory they do not know what about each other and deleting the memory then of course Forex havoc now the second step is we have to remove the pointers of the first one and for instance you set the pointers of the first so this temp vector to zeros that's great because this is very cheap this is so cheap that we do not really think about it as in operation itself so the idea is do the minimum amount of work to transfer temp into v2 and of course at the end of the statement okay by the way note this is only possible this kind of optimization because nobody knows about this temp except for this assignment operation no one all else holds reference to temp if there would be somebody else holding reference then we would have a problem all right at the end of the statement however temp of course goes out of scope and now the final result the vector we created in viju perfect now you already start to like it hey this is something that is cool so why don't we apply this to the first example well of course in this case we would have a problem in the code we would not see that v1 is transferred to v2 and this is the kind of the wrong word semantics but we can do something about this if indeed you're interested to transfer the content of e 2 into V 2 V 1 into V 2 and all we need to do is to use move still move is now basically telling us this is a transfer of content so we'll take a look at the details later but at this point we basically proclaim we want to transfer from V 1 to V 2 and we do exactly the same as before we first of all copy the pointers and the second step is again we just delete the pointers not delete we set them to 0 in V 1 V 1 however in this case lives on a little longer so but this is the detail so if you take a look at later right now just remember V 1 is still alive it will be an empty vector for quite some time well until the end of some scope all right let's take a look at the details on the left-hand side again we we now see the vector standard vector which is small excerpt from standard vector and and alright we see the example that we saw before the first statement that we analyzed was this one we P is equal to V 1 now this is the V 1 is now something called an l-value am i now he's starting with technical terms okay let's clarify the technical terms first at least a little bit there's more back to basics talks that go much into the into more detail here l value and that's also something called an r-value this is what people usually a little confused about so there's a historic reason why they're called L&R value the L in this assignment that is l value simply because it appears on the left of the assignment and the other thing they are that is an R value because it appears on the right strictly speaking an R value can only be on the right of the assignment and elderly could theoretically also be on the on the right side but well an elderly can be on the left side so it has a certain name this is however only the historical thing this is where the name comes from do not really remember this from an C++ point of view unfortunately it does not work well anymore I give you a simple example and say we have a string when s plus s is equal to s is actually valid code it will compile will not do something reasonable but it is valid code and it is kind of reversed because now the S on the right is an L value and on the left actually we have an R value so it's kind of reversed it's pretty unnatural it's pretty surprising again but there's a much better rule for C++ something that he can remember and networks absolutely fascinating well the thing on the right the S is now value because it has a name a name that you have chosen and identifiers that you have to liberally yeah put in to put those chosen for this variable and L value now on the left however you have an R value because it does not have a name so the result of s plus s this temporary you did should not choose a name for and hence it is an R value that's the entire difference between l and r value now v1 has a name hence it is an l-value an l varies well okay they bind to the assignment operator laticia on the left that is perfectly key this assignment operator now would do a copy because it assumes that you indeed want to have this copy everything's fine now let's go to the second assignment create vectors V 2 is equal to create vector prior to C++ 11 this would actually have created a copy so it is in our value we said this before prior to see plus 11 news would actually have created a copy because there's only one assignment operator for that case and that is I have to say was the essential problem I cannot distinguish I could not distinguish between the first case in the second case the first case the L value is copied the second case well I want to deal it with is differently but unfortunately as long as there's only one assignment operator it would also do a copy for that reason they introduced what you probably have seen before arveleï references so using two ampersands for a reference makes it a so-called arveleï reference an R value reference does exactly what it expect this arveleï reference takes our values so our values bind to r-value references l values naturally bind to l value references so with our value reference in play the other kind of reference is just called our value reference l value reference and now with an R value reference we have actually something to make a difference so the R value can now bind to the R value reference and this function knows about the fact that it is an R value this function now can assume that you want this to be transferred well it's temporary or anyway a temporary that is not needed afterwards and as I said before there's exactly one reference to this kind of fact is so this particular temporary so I can do a move so this operator the one that takes an R value reference moves all right now let's take a look at the third one we want is okay question to you what kind of value an L value it has a name however the move operation now makes it in our value I basically declare please treat this v1 as if it would be in our value officially not to confuse you but officially this isn't called an x-value one more term expiring value it's basically just for the c++ guys to have some vocabulary to deal with this explicitly but basically make v1 now some kind of our value because we deal with this as an r-value it will bind to the r-value reference exactly where we want it to be so it will be treated as non value and I move the content off we one into v2 now we one is well still alive it lifts on we have transferred the content from we one into v2 great this is what we wanted but we one lives on it has a name you can still use it afterwards and this makes it to some extent a little dangerous so officially the standard says this is a move from object I moved from basically says don't use it anymore yeah okay if you know a little more perhaps you have some ideas how to misuse it of course but the general advice would be just leave it be just wait until it naturally dies so at the end of its scope it will be just probably destroyed we it's destructor but don't do anything special with it anymore officially there's only three operations that you allowed to use on a move from object the first one happens implicitly that is the destructor the destructor of course will eventually be called and then as two other operations that he can do deliberately the first one is a copy assignment and a second one is a move assignment so you can assign to this thing again and suddenly becomes a valid object again you can again use it you can work with this properly but as long as you did not reassign it just leave it be the interesting question would be what does it move - how does move look like and you might be surprised to find that move is actually pretty simple this is move a move is essentially a steady cast okay so steady cast to bla bla bla bla ref ref and this is also what move returns bla bla bla ref ref so a move returns and our Valley reference it does not really move anything the only thing it does is it makes whatever you give it to an R value reference and henceforward is treated as an R value and therefore it binds to for instance the dis move constructor I move for Simon operator so move does not really move anything the usual question is does the compiler give me any warnings when I try to use something that is moved from now the compiler will be absolutely silent of this about this and as a simple reason static cast a cast is as you know an adult feature you know better than the compiler and if the compiler says well you know better then he will not worry about any misuse later so you are on your own here except of course that there's other tools so static code analysis tools might warn you about some access to some use of a move from object the compiler will not thanks to static cast all right so now you understand that move indeed does not really move anything but that this make a difference in terms of semantics it is a semantic transfer of ownership so some people actually argue that it should not be called move but something like transfer content transfer ownership would be much longer name move is probably a little nicer but this is the semantically part about it it is a move from a semantic point of view a transfer of content it is not a physical move nothing physically moves anywhere alright a short summary in between so containers in C++ employ value semantics this is something that we desire this makes our code easy to understand easy to comprehend we do not have to figure out where is sharing happening under the hood in presea plus 11 this little led of course to a lot of potentially expensive copy operations thanks to the introduction to our Valley references now we are able to distinguish between our ell values and our varies and became therefore react accordingly we can deal with ell valleys differently that we can deal with our valleys and arveleï references basically just represent modifiable objects that you do not know that you no longer need temporaries all the things that you have explicitly declared to be temporary and basically as politically said I don't need this anymore please transfer content please optimize alright now let's take a look at some implementation details there is two functions that you need to make this entire magic happening and these two functions I show in an example of a widget class so class widget has three data members an int it's a representative of a fundamental type a string as a representative of a class type and a unique pointer admittedly also class type but a special one it's a pointer type reference Mandrax and the two functions that I mentioned is these two the Move constructor and the Move assignment operator this is the two functions that are now also added to the set of special member functions so now in total we have six the default constructor the two copy operations that remove operations the new ones and of course the destructor the canonical syntax for these two or canonical signature of these two functions is one argument of exactly the type of the function a function is class ref ref this is indeed the canonical signature there's not a lot not a lot of difference you can make you can actually add a Const air but it does not really help you the purpose is that I do modify the object that I'm given so well a consistent help and as soon as you leave out any reference it would be the copy constructor or the copy assignment operator so this is indeed the canonical signature for this cars actually life is easy you simply can define these to default or perhaps even leave it to the compiler you do not have to explicitly mentioning them this is great all of the things that all of the data members are properly moveable the int okay this is fundamental the string also knows how to transfer content to some other string and also the unique pointer is equipped with the right functions so this is exactly what you would like to have this is the best possible situation there's a name for this so the coq there's a coca at length C 24 that if you can avoid defining default operations do and this core guideline also mentions the in official perhaps even official name for this the rule of zero this is where you want to live this is what you usually want to use your life is easy your life is yeah comfortable let's for the sake of this talk of course let's assume that I have to do something extra so let's assume for instance that the unit pointer is just a pointer and suddenly our life gets much much more complicated because now we actually have to deal with these two functions we can no longer make them default we have to implement them ourselves in order to make this work properly and let's take a look at the implementation details first of all of the move constructor well the goal of the move constructor is to transfer the content of the given object W into this object and of course we want to leave W so the passed object in a valid but undefined state so this does not sound great of course undefined is always something that is a little vague this basically means it can be any state it must be a valid state but we cannot predict the state that it will be in all right how do we achieve these goals the first step is probably just to copy the int okay no ill here no ill effect everything is working then well let's do this with a string too so let's move the string okay now a couple of people promise you know about this probably fainted now unfortunately this is not what we might believe it is in this line you actually do not move the string what do we do with the string we copy the string and this is exactly what we do not want to do we want of course to be as efficient as possible we would like it to move why doesn't it move who knows correct the string has a name pad also that W has a name so the widget that I'm passing has a name and because of that it is simply an L value and an L value is something that I cannot or should not move of course an L value since it has a name is still needed hence I do a copy however there's of course an easy workaround okay I should perhaps point out this this mystery here so yes it is an elderly that type however of course suggests differently which it refresh this is I admit a little confusing art reference tune our our value reference and then it still is nail value it may be confusing but it's easy to explain if you think about it like this it is indeed an R value in the calling scope at the point where we call its function it is an R value else we would not end up in this function however inside the function it needs to be an L value again if it wouldn't really if we do and half a name we could not use it and so well it has to be an elderly again but we can easily make this work again by using move we explicitly move the string to the other string and this is perfectly fine because we know thanks to the type that it actually is in our value so ultimately this is now moving the string properly now I apply the same operation or also to the int no it does not make any difference the int is still just copied there's no speed up at all however it is a nice and a pretty reasonable convention just move all your data members regardless of whether this is int so fundamental types or class types then it's first of all very heterogeneous and second of all in case you might see this in the remainder of this talk in case you change the type of this I you not have to go back here and and basically fix the code it will always work it will never do the wrong thing and so this is why I acquire this conventional in for the remainder of the talk all right and of course I also move the pointer it's not really moved it's also just copied there's nothing I can do better than copying it but as I said I know used to use convention we're not quite done yet I copied the pointer essentially but unfortunately at this point we have two objects that point to the same memory and the basic assumption of this widget is that this pointers in owning pointer so now I would have to destructors that would destroy the same content and this is bad so there's an absolutely necessary further line we have to explicitly set the pointer of the W budget to know without this it would break pretty badly now you might think okay so by the way we have now first of all resolved our first goal we have now effectively transferred the content of W into this now we're there the entire content of the right-hand side object is now in this perfect now let's take a look at more a couple of more details the first of all you might feel that these two operations well you always have to think about this you always have to remember that pointers are special that other the other pointer in case it's owning has to be said to know there is a little helper that is called stood exchange statics changes a function that sets the given pointers at WPI to now pointer and it returns the old value of WPI that is of course the value that I want to have in my own pointer that is a nice helper that basically makes this a little more compact and you might not forget this although it's nice just for educational purposes I now simply return to the old thing I want just to show a couple of more details all right there is another cool guideline make move operations no except so let's do this but let's also ask why is this necessary why is this something that is not like consider moving it now except but make it now accept so first of all it's actually not wrong moving the I is well no brainer this can never fail this will never throw an exception when also the pointer is a no-brainer it will never fail the string well we just have to look it up and find that the move operation for the string is no except to and so this is not a lie this is perfect this is no except also of course setting the pointer to no but there it's a good reason that the court guidelines point this out it has to do with performance and in order to demonstrate this I have now written a little example program so first of all let's say that I create a long string longer than necessary sing a small string optimization then have a vector of widgets at this point it is empty n is the number of widgets I want to create the next I'm running a for loop and I'm creating end widgets and I'm pushing back them back into the vector by means of move and then of course I measure the time this takes and eventually I just print the run time if I do not make my copy constructor no accept then I both clang in GCC they show exactly the same result solve this in 0.005 seconds market just a number at this point however if I use snow except then suddenly the runtime goes down by 60% to only a two point to see if it's zero point zero zero in two seconds and it's quite a bit that is not just a 1% or 2% difference this is very a very significant change the reason has to do with indeed exceptions push back gives you a guarantee it gives you the so-called strong exception safety guarantee if you if any exception is thrown in push back it is as if nothing has happened it is as if you would not have called the function this is a very strong and a very valuable guarantee but as soon as we use move as soon as objects are transferred I am changing my inner content and this is why push back has B to be pretty defensive if you have if you promise to not throw in your move operation then push back will indeed move it cannot fail it will not destroy the old state everything's fine but if you do not give this promise unfortunately it has to do a copy and so if you do not use no accept it basically falls back to the precip lastest 11 behavior which is pretty unfortunate but this is why the quadlings pretty clear make your operational accept alright so let's do this then what about this WI I have set the other pointer to now but I did not really set WI to 0 or some other default there is yet another core guideline c-64 a move operation should move and leave its source in a valid State this Cork Ireland has a note we don't read all of it it's this first part that's interesting ideally that moved from should be the default value of the type and show that unless there is an exceptionally good reason not to so if you read this then this basically means that we actually want to set this to zero okay so let's do this however this is not what the default would do so if I indeed would have a unique pointer if I would choose the default datian the default would not set this to zero so if the default is not doing it apparently it's not such a bad thing of course by setting this to zero we have now also perfectly achieved our second goal w is now invalid but undefined state actually it is in a default state which is what the core guidelines suggest but if you leave this operation well it's also in a valid State you just did not reset the int to zero it's just the value that it had before and this is also perfectly valid state however undefined from a higher perspective because I cannot really predict what it will be from a performance point of view however it's of course reasonable because if I don't really have to set it to zero then leaving the out is just a tiny little bit faster so this is now kind of the canonical move constructor this is what I would argue is what you would have to write if you indeed have to write it yourself all right now in this canonical form you see some like two phases phase 1 and phase 2 and phase 1 I do a member wise move all the members are explicitly moved from W into this in phase 2 I'm explicitly dealing with pointers pointers are unfortunately special pointers have to be handle explicitly so I have to say owning pointers if it's not an owning pointer you'd probably don't care but an only pointer is special I have to deal with this explicitly so please don't forget this this is a usual a usual kind of problem if I however make this a unique pointer again so going back to what we started with then what I can omit is phase 2 by the way now see I change the type I do not have to adapt phase 1 so I moved WPI and this is still exactly the right thing to do and I'm only left with phase 1 if this is what you only need if phase 1 is all you need indeed then you are fine with the default the default move constructor this only do face one a member why smooth move number one move number two move number three and so on this is what the default would do and note the default is also no accept and so you don't have to worry the default gives you the whole package and as I said this is exactly what you would like to have this is the perfect class design following the rule of zero all right now going back to this this form that I said is the canonical form for an implementation that you do of course there's other variations of course there's options you have I cannot show you all possible implementations I can only show the canonical form and hope that this is what you use most of the time if indeed you have to write this function yourself I would follow the advice of how Hinnant I think the most important takeaway is that programmers should be leery of following patterns without thought so think about what you really need think about how to transfer in a most efficient way the content of the other object into this if there is something better you can do then this canonical form of course do it this is all about performance this is all about efficiency and you can do whatever is necessary to be as fast as possible alright having covered the move constructor let's take a look at the move assignment operator question so can you see Mike thank you you go back can you go back to the page with a unique pointer yeah so we when you stood moved P I did that automatically reset double use unique pointer or leave it alone because this two moves just a static cast correct basic you answer the question yourself if this move WP I would also nicely reset the WPI to now oh this would be nice a lot of people are usually asking me wouldn't this be would need to be exactly what I want there is the problem with non owning pointers an on owning pointer perhaps should not be reset and so there is no general move that always resets the other pointer it depends on what you really want to do in case of an owning pointed there for you unfortunately have to do it yourself and you have to think about it else it's usually a serious bug but no it's not done automatically the string is different because the string of course has the move operations itself this okay this one sorry then I was misunderstanding this one so do I have to move the unit pointer so this this version yes I have to the unique pointer again it would be an l-value I would copy the unit pointer okay now I got the question completely yes the unit pointer is completely reset absolutely that is part of the class okay so for the misunderstanding yeah so the unit pointer has a class type knows exactly what to do it resets its internal pointer automatically and this is exactly why phase one is perfectly enough you don't have to you don't have to do anything class types are much much easier to handle than pointers all right okay all right then moving forward let's take a look at the move assignment operator and of course this one it always Christian I'm sorry yes so if you have if you have this case where it's an owning pointer are you gonna if you try to use the default move constructor are you gonna get a compile error if not what are you gonna get okay how can you send you it so Mike it's not particularly good okay sorry yes so if you have this case where you have an owning pointer and you use the default move constructor are you gonna get a compile error and if not what are you gonna get so you're asking whether that in the default constructor if I get an error to do that no no so if you don't him if you don't implement it if you just said equals it's just so bad there's so much echo that so you have to find the right distance okay if you just say equals equals AE equals the fault okay yep are you gonna get a compiler because you have a type that's not movable okay so let's let me find the right slide now I got it this one well but instead of the unique pointer you had a raw pointer okay basically if I have a pointer now with default do I get a compiler no it could be that it is perfect if it's not only pointer perhaps this is exactly what you would like to have so unfortunately this is not a compilation error it is no winning oh you forgot to deal with the pointer no unfortunately no it's not a lot of help here so you have to really remember yourself okay other questions while I try to find the slide again all right they move for Simon operator it is very very similar to what we've seen before the move constructor first of all we should think about what we wanted chief it is a little more we now have three points the new point is the first one we also want to clean up all visible resources in the in our yeah in our own object we want again to transfer the content of W into this and leave W invalid at an undefined state so it is similar but it is a little more but it is reasonable because the assignment operator is of course changing an existing object versus the constructor is creating something new the constructor is usually simpler now the first step we do is again we move the int again I use the convention to always move yes again this does not do any kind of speed up but it is just a little more canonical then we also now do it correctly remove the string again if you omit the move here we do the wrong thing it would compile it would work but it would indeed do a copy so we correctly move and then we move the pointer also Kay but now in this case I have introduced another kind of error I have now over written my own pointer my PI previously there was some value in there some value to the content I own now it's gone so what you must not forget in the move assignment operator is to explicitly delete PII also this is not part of the move thing this is something that you have to deal with yourself so usually the first line is delete and not just the first line if you've more resources than the beginning the beginning block is usually just what you do in the destructor as well so unfortunately there is currently some kind of duplication all right so we dealt with our old resource so we cleaned up all visible resources properly perfect first goal is set then again afterwards I reset the pointer of WPI to null pointer now at this point we have again effectively transfer the content of W into this great again you can use stood exchange it makes the code a little easier to read and you do not tend to forget to set the pointer if you make this the habit exchange pointers but again although it's great for educational reasons I just show these two operations explicitly now in this these two operations now some people use a different form they kind of collect these three into a swap what he can also do is you swap P I and WPI this is still unfortunately a common way to implement the move assignment operator it is unfortunately unless little less deterministic so there's a couple of arguments that usually against this form yes of course it's very elegant it's very short pretty expressive but in this case you are transferring your old resource into W this is fine it will be taken care of eventually but what you don't know exactly is when eventually at some point in the future this new resource will be destroyed but since you cannot tell anymore how long it will still stay alive it's well less deterministic also if he is counting operations admittedly just pointer operations there's one point operation more and so it's a little less efficient yes it's elegant but ice tend to just recommend use this form explicitly all right again please so assuming as you're clean not be resource before you do the move this would all go horribly wrong if the the the reference you were passing in was actually a reference to the object you were swapping into moving into okay I think fortunately on got half of it I'm sorry sorry I'm really very nervous so I miss you cell facade all right Simon so with when you do it would only happen if you had to stood move on an l-value asu's because if it's an r-value is a term okay this is a very good question is that with when you do that static casts to an r-value would I still be able to have a check to check that it was not the same as this all right actually it is so self-assignment it should have moved to self this is possible of course I could simply write something like W is equal to move W this would obviously be a move to self what would happen in this case well let's think it through delete P I I would delete my own resource first all right now it's gone now I simply copy my pointer into my own pointer I move my string into s now of course this depends on what the string does and then I move my own pointer into my own pointer and set myself to now so essentially afterwards I'm just kind of in a default State not quite because I copy the pointer so it'll be int but it's like as if I moved from move to self is different than copied to self if he copied herself so W is equal to W we assume that nothing changes the object is still in the same state this is the basic assumption move to self however basically means we have no assumption about the outcome what what should it be w is equal to move W you explicitly said move W you basically say I don't need this guy anymore so this is actually perfectly fine I do not have to protect against self assignment afterwards it is moved from if you feel uncomfortable this of course you can add an if statement if address of right hand side is not equal to this then do this also okay the result is valid but undefined so move to self is indeed different than copy to self now you can feel comfortable by just making it as fast as path what must not happen however is of course a crash a crash is is of course out of the question okay question so he said she moved her shelf is in defined okay a little louder sorry so move to self is that then considered in defined behavior no move to self is not undefined behavior afterwards however this value is in an undefined state as usually it moved from object I moved from basically means okay it could be in any state I don't need it anymore I just leave it died by the way W is equal to move W this is probably the only situation where you obviously move some valid object into itself I am only aware of an one other situation we move to self happens and this is in a swamp in this in a second move operation inside a swap swap usually three moves in a second one I move to self but I move from Ana moved from object into a move from object if I indeed do move sorry in a swap so also in this case this is perfectly fine you do not really have to protect against move to self perhaps it's a little counter into but this is also what you find as the common advice I should mention there is a core guideline this Courtney core guideline says protect yourself however there's a note in the core guidelines exists well of course we try to be a little defensive we don't know what could possibly happen and therefore they take the point do the if however I have never seen it fail anywhere so this is indeed kind of the canonical form for the movie Simon operator okay no except also don't forget this and of course we can again assign the integer this is again purely optional the default would not do it so I just leave it we have again achieved all our goals the object is now in a valid but undefined state in this case you see three phases the first phase is kind of cleanup yes to some extent a repetition of what you do in the destructor sorted at which structure differently but yes it could be a copy of what you do in the destructor then in Phase two I again do a memorize move move number one move number two etc and phase three is again an explicit dealing with pointers this is something that again you cannot forget as soon as I make the pointer a unique pointer again I can omit phase one and phase three the unique pointer knows full well how to do these operations and again if you're left with this one phase if it's just moving all the members then you're fine with just using the default so in this case I could again say default and again note is this no except again so it is much much easier and much better for you if you can rely on the compiler generated default there's one trick that I want to show you one possible implementation of the move assignment of it it is slightly different actually this is not the move for Simon operator anymore strictly speaking it's to copy a Simon operator but if you write it like this it could actually be both it's like the copy move Simon operator I passed widget by value by doing this you can actually just swap the term the widget internally and then you're done if you pass in the assignment and L value the argument would now be copied I would use the copy constructor and then I would swap the copy if I would have an r-value so some kind of temporary then for the construction of this temporary object in the the object here in the argument I would use the move constructor and so I only have to write the move constructor and here I base it here to move for free it is not as efficient as you would write both functions but is it ice trick if you just want to quickly cover the operations so it's kind of a nice fallback if you don't want to sketch out everything right away all right now that's just one more detail that that I want to add when does the compiler add these special member functions a question so I have now implemented my own swap I did mention this I'm sorry so I now have a swap in the widget so one argument not to in this of okay now if in case you use Smoove in there okay then it certainly is different if you add your own swap and if you just say okay is what pointers I do an int whatever then of course it would work yeah so not still swap your own swap now which you might have anyway alright when does the compiler generate these functions well there is a certain well some arcane set of rules the default move operations are generated if no cop operations all destructor is easy to find and then move the default posada for corporations are generated if no move operation is user-defined well note it will afford an equal delete officially count as user-defined user-defined basically means you mention these functions in your class you do not necessarily have to implement them yourself you just write them like here into your class all right let's play a little game one minute let's assume I have this class X I want to have a virtual instructor this is why I write the destructor myself but the defaults aren't entirely fine so I say equally fault does the compiler now generate the copy operations yes or no okay to make it a little quicker who says yes okay a couple of people say yes anyone with no so when does the compiler generate copying the default copy operations are generated if no move operations user-defined at this point I did not deal with move at all so the compiler would generate the copy operations very nice what about the to move operations no move there's no move because I have usually find a destructor so I have to admit for virtual class I don't receive point of move operations but for the sake of learning let's say that we need them and that I want to default them so now I add the two move operations to my class what about the two copy operations does the compiler generate the to copy operations so no unfortunately it does not because now I have user-defined the two cop did to move operations so now I have to get them back by defaulting the cop vibrations to now what you see is something called the rule of five or something that you see in psycho guideline 21 if you define a delete any default operation you finally lead them all so commonly this is known to be the rule of zero Epis sorry you rule of five or also if you also count the default constructor the rule of six so either you live in deep arrested before buying the rule of zero or you better define all of them this is easier easier for the readers of your code they will not want wonder do you know about these arcane rules are you sure what you're doing because indeed they are a little I came all right that is the basics of move semantics there is just one more core guideline I want to show c15 prefers simple and conventional ways of passing information so you might not wonder this move semantics doesn't appear in in many places do I have to spring to my code with our early references likely no it is indeed something that you can consider an optimization C 15 gives a very nice list of yeah conventions that you should use for return values and passing parameters and he promised the advice here you will find move only in two places you'll find this in two special cases in entertain copy and in and move from special cases there's an input parameter and I need a copy in this case you can optimize you can optimize based on whether it's L or R value and any move from is yet more special case I have an in parameter and I know that I move from it okay then is this know with our references easy to deal with by just taking this as an r-value reference so the place where these are value references creep up is primarily the move constructor and move assignment operator this is the two functions where use the most often in other cases it is primarily for optimization purposes all right notice we are basically at the end of the first part and this is what I hope you now feel like you now feel like okay and I thought move is difficult it's just copying couple pointers here Nakia have to should not forget to set a couple of pointers to zero but ultimately it's not that difficult and so hopefully this is the feeling that you also have if you come back to the second part where we climbed downhill a little bit okay thank you very much [Applause]
Info
Channel: CppCon
Views: 59,859
Rating: 4.9609609 out of 5
Keywords: Klaus Iglberger, CppCon 2019, Computer Science (Field), + C (Programming Language), Bash Films, conference video recording services, conference recording services, nationwide conference recording services, conference videography services, conference video recording, conference filming services, conference services, conference recording, event videographers, capture presentation slides, record presentation slides, event video recording, video services
Id: St0MNEU5b0o
Channel Id: undefined
Length: 55min 17sec (3317 seconds)
Published: Sun Sep 29 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.