CppCon 2019: Ben Deane “Everyday Efficiency: In-Place Construction (Back to Basics?)”

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right I turned my mic on the room good all right then I think we're about ready to start thank you for coming to my talk my name is Ben this talk is not about type design which it was tagged in the program so sorry about that however it is about in-place construction something that a c+ plugin a C++ programmer of one weeks experience can understand the idea of but of ten years experience can still find tricky and it's based on I develop this talk maybe 18 months ago based on seeing a lot of code in a relatively large code bases where engineers were trying to vault kind of skill levels we're trying to adopt modern features we're trying to use things like in place are in place back or you know we're trying to get that efficiency that C++ as C++ programmers we want but falling into pitfalls you know not quite getting it right or rather or maybe a refactoring would would change things up and cause what was good code to become you know not so good code so please ask questions as we go this is what we're going to cover I'll either answer it on the spot or defer it to if you just slide if I'm going to cover the point first of all I want to ask who here is using let's say at least C++ 98 hopefully everyone yes all right at least at least 11 keep your hands up just drop them at least 11 pretty much everyone at least 14 okay mostly people most people on footing at least 17 okay so that's the big break point and anyone using like bleeding-edge like beyond 17 as much as their compilers can support great ok so most of them there's even some parts of this which are applicable to 98 it's pretty much all applicable to post 11 and you'll see that star in the corner when let me see a slide with code on it that star indicates which version of C++ I'm talking about although many of the many of the slides that are on 17 if you're on 14 you'll find that there are applicable things to 14 okay let's get started I'll also be how'd this come out can everyone read that my syntax highlighting is a constant battle yeah if we could dim the lights a little bit it might help anyway this class is something you might have seen if you've watched Jason Turner's you know see CPP weekly it's just a noisy class that tells us whenever any of the special member functions get called and you're going to see this a lot use of this a lot later so in particular I've outfitted this class with pretty much all the special member functions of each we could want and importantly multiple constructors some of which are explicit some which are multi argh because so so hopefully this covers most use cases that you would find in in code bases of your own all right so let's first of all talk a little bit about what happens when things get moved because I had this realization I know a year or two years ago moving from a string usually isn't any faster than copying it and if you if you're wondering if you if you're if you're wondering are doubting that then think about the reason why we have the small string optimization most strings are small which means most strings go in the small string buffer which means mostly when we move a string is gonna be moving a small string which is going to be the same as a copy and all three major STL implementations actually will zero out or I should say write a zero so make the length zero of the move from string so it's slightly less efficient sometimes to move a string than to copy it and let's see if I can make this quick bench thing work if I go to there I'll show you that tab you can see there that the copy was slightly faster than the move for a small string about 20% faster in this case now you know benchmarks are always micro benchmarks so and this varies with the compiler and the Lib C plus and the standard library implementation you use but it's just a sort of surprising thing perhaps alright let's go back to the main here okay so why is our vo so important moves aren't necessarily cheap we saw that a string move isn't necessarily cheap here is an example where actually our vo does happen in this case so the return value optimization that is also known as copy elision phone book is a map we're returning it hopefully we're getting our view and in this case indeed we are however if we were not this would be a move mandated from C plus 11 the compiler is required to try and move the thing if it can't copy alight and if you're using Microsoft's STL and map is a node-based container which needs a stable end iterator which means that its move constructor must allocate so if you accidentally don't get our vo and you incur a move you'll get an allocation and that's well pretty much rule one of optimization is don't incur allocations when you don't need to this tweet by Billy O'Neal so our vo is really important so let's talk about it probably the most important optimization your compiler does and so important that even though it's only been mandated on our values since C for plus 17 all compilers or production compilers if any of any note certainly all competitive production compilers have done it for literally decades I was I was trying to find out when it started happening I asked Jason Turner he can't find a compiler history it gets it gets very difficult to run compilers before about 95 but I did find that named R vo went into GC GC C 3.1 that was about around 2000 issue and this is how our vo works just a quick recap so when the caller calls in to a collie there's this there's this extra hidden parameter which is the address of the return value here it's ampersand s in the collie stack frame and if the collie can return can construct what it's going to return directly in the caller stack frame yes there is no need for it to construct and then copy at the point of return so a here represents what what would happen in the non our vo case normally about our vo you'd construct and then at the return you'd copy or move now but when you can construct it directly in the call a stack frame that's our view so now that you all know that keeping that picture of mind allows us to think about when you know exactly how to make our vo happen and in particular how how we can accidentally inhibit our view so there are two things to consider what the standard says and what's possible and the standard says that we have to return either a temporary guaranteed after super 17 or the name of a stack variable and then we can think about what's possible so those are the rules then we actually have to have the opportunity to do it so sometimes you can't you can't do it if there's no opportunity to if you're not actually constructing the thing then you can't construct it in place if it's not the right type you can't construct the thing of the wrong type in a slot for a different type and if you just don't know enough at the time when you're constructing to get it done so here's some examples of this here's a function that can't rvo because what it's returning is what it's been passed this is a fairly famous example you can't obvio on function this is quite well no because you don't construct them but you're still going to move since C++ 11 here's another famous example stood move in that return statement is almost always an error there are a few cases when you're writing value wrappers and things like that you will generally know them when you when you when you are doing that but of course student movie gives you a value reference that's a different type from the thing you're returning no rvo for you and an example of the third cases you just don't know enough to rvo if at the time you have to construct the thing you're returning you don't know you don't know which thing you're returning like here there's no possibility that you could construct it in place all right now I know it's late on a Thursday I'm just going to ask you some questions and they if they see something simple they are so just shout out will this AVI oh yes we're returning a temporary that's plain good all right how about this yes yep yep this this is this is still retaining and temporary and this works you know returning a temporary is in general much more reliable than returning a name thing it generally works even in debug builds how about this one no no it's a function argument you're not constructing it no opportunity to do so how about this yes yes in both cases because you're returning an R value in the in the bottom line you're returning just a temporary that you make then the top line it's a temporary formed by the function call you can rvo right down the you know like copies down the call stack how about this one this is an interesting one isn't it so it depends on your compiler this is an optimization opportunity that clang can see and you see CNM SPC can't at the moment so it in the case where B is true it's not required and I guess in the case would be as false maybe it is required this is a mix of cases isn't it but basically clang clang does this GCC mm SVC can't see this one this is the same as the previous one except I hoisted the construction out of the if and this is none of the compilers can see this but you know maybe someday they might because there is opportunity here okay how about this one no somebody said no we're not returning the name of a variable and in fact we're not returning a thing of the right type because the ternary operator in this case will produce a thing of an L producing L value reference so actually we have the copy here we can't even move so this definitely defeats our view and you don't even get the move so yeah and it's it's against the rules somebody said C++ is garbage I had something how about this one this is similar to the previous one yes this time both sides of the ternary operator are our values so the overall type of the ternary operator it turns on our value we get copy elision we're returning a temporary yes yes this is plain named avi oh this is you name the thing you return the thing all the compilers are fine with that how about this this is a trap for the for the experts so I know what you're thinking you're thinking you put parens around the s and it turns it into an expression which is an L value reference which is a different type but the standard specifically says that you're allowed to do that however GCC failed to do this if you're telling it to use a standard after C++ 11 if you tell it to you understand the C++ 11 it does it I think this is a bug or a regression in later standards for GCC sorry yes no I do not reckon there is no so I do not recommend putting your return values in parens outside the very specific use cases that you might have but in general no there is no valid reason to do this really and finally this is a little different again if you have a write code like this I'm not gonna let you through code review but everything here is complex bura and in the case where we get a construct we'll return zero in the case where we get a move in this case we'll return one anyone know whether we'll return one or zero will we get the constructor will we will we not it looks like we should get zero but again the standard actually the standard says you can't you don't get copy elision in context for contexts that's not maybe something that'll happen forever but currently that's what the standard says and so this depends on what you write what this depends on P right if you put context for a P know if you put Const P probably know if you put Auto P it depends on how aggressive your compiler is about putting things in context for context GCC is very aggressive you will always get the move here with GCC clang less so if you make it not Const you get them you get the in place if it's constant you get the move so please don't write code that relies on observing this distinction okay I told you that I saw lots of code really trying to trying to be you know trying to take advantage of this this is an example of code that really tries it's best to get efficiency and it's really doing it is getting inefficiency but it's just doing too much it's applying stood move to the result of a function call well that's another value anyway and it's you know it could be simpler more simply rewritten as just returning the result of the function in fact the stood get observers of the function okay before we continue question yes right in this case unsigned long long you know a mover a copy or whatever doesn't matter that's true it was more of a comment that that the unsigned long long it doesn't really matter in this case whether we get a copy or move but the the visual noise in trying to understand what this function does with the stood move and the extra line I think it's worth trimming down because you know in this case it's none time long long but someone sees this they copy the pattern somewhere else in the code bases where it's not an unsigned long long their place did move to result of a function call it's just visual noise it's not get it's not gaining anything okay now before we continue if you haven't already thought there will come a point in this talk fair warning where you wait you think why am i programming this language that's so complicated I'm not sure but hopefully by the end you know my point in this talk is actually I don't like default construction we can get in place construction always but we just have to jump through a lot of Hoops sometimes okay let's talk about putting stuff into a vector because this is kind of like everyday bread-and-butter of people's code bases when should you use pushback when should you use in place back there trying to avoid the tendency of thinking that in place back is magically better than push back so this is what a place back and push back look like for the the signatures that is from C++ 17 in place back returns of reference prior to 17 it returns void and you can see the pushback is overloaded on L value and on our value references so with that in mind which of these and imagine that these are happening either raw because I know you shouldn't stood move something and then move it again right well then use it again even but which of these are these B's any different yes practically no right practically no the path to the person who said yes yes if you're in C Papa 17 that's the difference right you can capture a reference to the thing that was that was in place backed and placed it back which whichever way around you say that okay now there's a difference yes because and this is the whole point of in place back as we know what we're passing is a con star star it's not enough idly reference to a string or an l-value reference in place back is doing the job it's supposed to do it's constructing in place from the arguments forwarded to the constructor of string now at this point you know there are some people who say well just always use in place back because it's strictly more powerful than pushback right some of you might even say that and I would say to those people I don't like that view I because so I don't log into my computer as route I like to use but I like to use the least powerful thing that's available to me and this isn't just a philosophical point of view this helps the reader of my code because if they see me using push back that's that's saying that's me saying to them I know I'm gonna get a move here I know I'm gonna get a copy here I can't do any better my code is confident in the words of Kate Gregory and if I use in place back that's me saying I'm actually trying for an in place construction I know what in place Peck does and I'm using it because I need its power so that's why I prefer to use push back when that's all I need all right so of course in place back takes a parameter pack so one of the other useful things you can do with it is to do default construction right which you which you can't otherwise parameter pack can be empty that's sometimes useful and because it takes a parameter back and perfectly forward it sags it can also it can also take advantage of explicit constructors so if you remember that class s that has all the noisy stuff about the special member functions it has an explicit constructor from int we can't push back an int because that's push back you know can't implicitly construct a simplicity can't wrap out the call site because it's looking for an S and you can't implicitly construct remand int but in place back works okay now we've got we're trying to copy an array of arguments to SS constructor and make an array of or a vector of S's what's going to happen here this is typical code again I've seen I've seen you know a transform or a copy something like that trying to make type type of one thing from type type of another thing what's actually happening here but back in Sirte is gonna call pushback right so for each of these three things we're going to get a construct a move because it's an r-value you're going to trigger the r-value overload of pushback a destruct of the temporary that you constructed and and each of that's gonna happen three times right so you're getting an extra move you're not getting in place construction so how can we get in place construction yes yes okay given that we're using c begin and c end here we will get yes you're right we will get a copy and no no we won't get a copy because we'll get because it we're not copying argh we're not moving argh we're using our to construct the s and the temporary s then gets moved yes thank you so what happens so that works but it's not efficient what happens if we have an array of hints now it doesn't work because as we saw before there's no way to implicitly construct an S for a million that constructor explicit so push back back insert I can't do that they can't push back so what do we do stick in a lambda right again I see this encode all over the place and now we're like well that's great you know and this this works with and it's doing the same thing as before basically construct move because the lambda ovios so we get the we get the r-value we get the move and unfortunately we can't do better than that with with back in Sirte because the library doesn't have back in place er here's a sketch of it I mean in reality these kind of iterators have a few more things that they they might have but these are the essential parts if you need a back in placer this basically works so instead of calling push back its assignment operator will forward the arguments just like in place does and they'll call in place back and then and then you if your pride to see Papa 17 you give it the make a function or you give it the deduction guide if you're on 17 and now the compiler is happy and now this achieves in place construction because in place it just does it place we don't get any default constructs or you don't get any temporary constructs we don't get moves we just get in place construction good that problems solved all right here's another snippet of code and you know like we could we could say a few things that wrong with this code it's actually got a few things wrong with it right so a vector of string view okay string view we're colleagues did move on the string view before we pass it to in place back that's what for a start string view is probably a value type we want value semantics on a string of you because this Jen is to just a pointer and the length the stood move is superfluous in either case and if we're calling in place back shouldn't we just pass stuff into in place back it's the kind of code I've seen in place back if you're calling in place back it's a code smell to be explicitly asking for the thing to be constructed at the call site because that's that's just kind of wrong if you want the thing to be constructed what you're passing to in place back it better construct it because that's the job of in place back right so don't explicitly call the constructor at the call site when you're using in place back all right sometimes sometimes we use vectors of pairs as maps so we know how to construct a pair but what if the the mapped type ie the second argument the second part of the pair has a multi argument constructor this kind of code is very common you know either push back ER and place back do the same thing here you're explicitly asking for value to get constructed so you're going to get value constructed funnily enough and you're going to get a construct a move and then in fact you're going to get a temporary construction move into the pair pair gets moved into the vector right you can get struck construct two moves you don't need extra moves so but what do you do if your second argument of your pair needs a multi-hour constructor anyone yes piecewise construct so pair has this piecewise construct constructor so it's basically a tag type and it's of type piecewise construct T and there's a value which is spelled piecewise construct and then you forward as you forward the arguments through a tuple that will used to be used to construct the first of the pair and the second of the pair so this is where you're starting to think C++ is verbose but this is what you have to do if you want to achieve in place construction you you call your emplacing you're constructing a pair in place so you need to give the arguments for pairs constructor which are the piecewise construct and the forwarding the things to your actual first and second constructors and it's it's verbose and I know slide where it's not particularly compelling but C++ is about know distributed fat right so when you need it you need it and you can achieve in place construction okay so that's about it's a vector and here my recommendations for vector as I said pushback is perfectly fine for our values and it's confident code and it's telling the reader I don't need the power of in place back and and the anti pattern here is asking for an explicit construct when you use in place back so look out for that one all right let's briefly say a few things about initialize a list William Shakespeare got it right so when you write initialize an initializer list it's as if you wrote a Const array and then a view into that array and that Const is important so I discovered this and I was a little confused with it at the start this anyone know what's happening here so it works fine that the one that works fine is fine and then there's another one that works fine until it explodes do you see why well it's undefined behavior which is to say it probably works fine right because in F in F there is a local variable of type Const int array right and that's what's being returned is a view into that local variable so that's a we know that's a bad thing use it returning views into local variables that's dangling right except they've got the that thing is const and it might just put it in your static data segment and it's fine for the life of your program I'm making the initializer list in F that's exactly as if I had written constant Vint is this is a bunch of hints and then I'm returning the view into that local constant so I'm returning a stack variable except that the compiler may because that thing is Const make it live for the lifetime of the program this is still undefined behavior but the compiler might might you know do that do the wrong right thing okay so initializing this has contour e this also means as you probably know that you can't put move only things in initializer list it just it just doesn't work you can find ways around it by faking initializer list and basically declaring what it would be yourself taking the constable a what you say or the engine is on your team's say initializer this is so convenient why can't hide much rather write a one-liner everyone can read rather than you know there's this horrible verbage so I mean you can make it a bit better you got you get you can turn copies into moves like I said a couple of slides ago but it's it's not that great to be honest initializer list is a bit broken there is a proto proposal by sy brand and christabella co-authoring i don't think i don't know when this is going to hit a mailing near you maybe not soon but it would be nice if vector had an in-place constructor maybe the worst example of this kind of abuse I've seen is something like this a massive initialize a list of strings and this is just this is incurring for every one of these things you know construct copy because you can't move out of initializer lists when you know when you when where you to use this Jason Turner has a great talk about initializing list I'm not going to say much more about them other than that just they're broken there is a there is a kind of caveat I highly recommend watching his talk because there is a caveat around strings strings mess with our intuition we we think about you know delay construction as late as possible delay allocation as late as possible if we have and typically when we can certain strings we have like const char star right so at the point where that's declared the compiler knows the length of that and if it can capture it in a string or string view it can continue to know the length of that but as soon as that char star decays as soon as that char array to get the case of a child pointer sometime later down the call stack it's going to have to course trill in and that means their various things might be faster or slower I don't think I have time to show you this this is in Jason's talk but just be aware that strings mess with your your mental model of how how fast construction is or should be okay so recommendations for initialize a list pretty much only use them for literal types I think use them for in season for floats they find for that anything else I don't really have a good recommendation for initializing list there's not many good answers so first point at the last point stand what if the result is constant there is if you make everything Const I'm not sure your mileage may vary I either way I wouldn't really advocate using initialized list for for more than like register types all right let's talk about putting stuff into a map this is where it gets a whole lot more complicated I'm afraid and when I say map I mean any of the standard associative containers so set I know that said map on all the map multi maps things like they all have very similar interfaces and more so their interface is also unlikely to you know be similar interfaces to any non-standard Maps you might use because people tend to copy the standard so it's perfectly possible to initialize a map with an initializer list is it good how many constructs copies and moves are we getting here remember we've got a map of s and that's got an implicit constructor for a mug we're getting actually forget how many I think we're getting two extra moves again the same as we saw in the vector case in this slice of this stuff is from the same in fact sorry I'm getting a copyright to this initializing this can't move out of initialize a list now an alternative to this is something that was presented or a few years ago now by Vittorio and he presented this for each n args function it's a function template and it perfectly forwards its arguments in batches of n to the function that you give it so and you can find you can find this function online if you look for it it's quite a handy thing to have when you're initializing Maps so you can give it a lambda like this and just tell the lambda to perfectly forward the arguments to the map in place and then you just give it a bunch of key value pairs as the 0 and 1 here and then and it can call explicit constructors because it's using in place which can take a bunch of explicit constructors and again if you need multi argument constructors for your mapped type typically typically you don't need them for your key type but you might need them for your map type then you can use this lambda with the piecewise construct argument on pair that we saw before now I tried this this talk at this point is about you know I tried when I originally made this talk I was running with Vesey 17 I think pair is a tricky thing Paras ask any standard library implementer I think actually Louis said it in his talk like pair has 18 different constructors and it's horrible some of those constructors are conditionally explicit and when I tried this with vici 17 it complained that vici 19 doesn't complain now so just be aware that you might run into some weird error messages with with pair and conditionally explicit construction but this ought to work and does work with modern compilers okay so we can initialize maps we're using this very useful for each end Alex thing I recommend that so how about putting things into an existing map you know you initialize map once but typically you put things in them a lot well if you're if you work in code bases like I do you see a lot of people using just the easy way how many constructs move on copies do we have here for example now if you know that that thing is already in the map this is actually not bad pretty fine you'll get you'll get a construct and a move sorry I move a sign in this case right because that thing's already there you'll get a construct and move a sign and instruct and if the thing's already there you can't you can't do a lot better than that I think but if the thing's not in the map that square bracket operator gives you a default construct and interestingly that that's pretty much the only method on map that requires your object be default constructible so sometimes you have objects that you don't want to provide a default constructor because it doesn't make sense you want to avoid you want to avoid this so you can use insert instead hello yes yes yes you do need an assignment operator it's an assignment yeah it's an assignment operator on the maps type of the bear the hog the s s has an implicit constructive remark right but not from int which is why I have the explicit construction in the first case so the other thing you commonly see in code bases is trying to use insert and this there are various alternative ways to do this none of them particularly good some which work better than others so the first case here you will get well first thing to note is if you're asking for a s to be there you're going to get an S constructed the compiler doesn't alight like it doesn't do reverse rvo in the way in a sense so you can get the s constructed you're going to get it moved into the pair and the pair moved into the map in the first case the second case have we're being tricky so we're saying this is not a pair of in the nests this is the pair of in and s our value ref so you'll get again the s gets constructed doesn't get moved because we said this is a pair of and it's an r-value refs are pairs going to use that fine but then the pair gets moved into map so we saved a move on the second line by trying to be clever the last line looks like it shouldn't compile but does because pair is trixie and has explicit can all conditionally explicit constructors and this is one of those times where it actually kind of looks weird but but this actually works fairly well sorry that last line I believe let me try here mm-hmm if I click on this maybe it will work so unfortunately I can't see it so the first line gave us explicit construct move move right construct move move second line we saved the move we got explicit contract number one move last line we got in place construct new moves so although that looks weird and like it shouldn't work it does in that case like I said pair is very tricky okay okay you say but insert I want to be clever in use in place because insert just looks weird what's the problem with this the principal smell is that we're asking for an S like never asked for the thing that you're trying to in place because you want it to be constructed in place if you just asked for it right there it's going to be constructed right there and you'll just get a constructor to move you won't get an in place construct that's exactly the same as you saw before this is the proper way to use in place as you probably know it's gonna try and construct a pair directly in place in the map passing 0 & 1 this gives us in place construct Richard what if you're keen values have multi arguments for their construction I'm glad you asked that what if you want to default construct the value any of your mapped type you you can't just give it one argument the compiler says none of the two overloads could match all the argument types right and it's the same deal if you want multi are constructs well before you consider that you can do this for a default construct don't do this yeah I'd really like there to be no discard on operator I'm just going wreck it okay so if you have a zero are construct or indeed a multi I'll construct you fall back to using the piecewise construct on the pair in this case a zero I construct right but if you have multi Oaks same deal here's an example of where this comes up in real code now here we have a set of client records client records have a constructor that takes these three things we're replacing it this is great this is correct usage right this passes go review everything is good then we want to upgrade the set to a map rather than having just a set of client records we want the map from client IDs to client records or some weather so the program that comes in they say I know we'll do this this now falls into the trap this is a pitfall right this gives us construct we're asking for it right there we get the construct or right there you can't see that one but we're asking for client record so we get a construct and two moves just like before so piecewise construct is what you need so it's the bose but you know there's always a way to get in place construction but it's so easy to let a change like that passed by in code review and you know accidentally incur cost what do you do if you want to emplace the result of the function call write it so it's quite complicated to construct one these things maybe you want to call a function to do it the problem is in C++ function arguments get evaluated before we call the functions right so this argument which is get s that function is going to get called we're going to get an S there and our value yes but we're going to get a construct and move we cannot avoid that we can't avoid that function being called but we can control anything we can control pretty much as the result when the result of that function call becomes an S and this is something that Arthur has blogged about and Andre Kaminsky so this is a little wrapper that wraps a function it captures your function and it has a conversion operator to the result type of the function so when you call it it will it will not create the S right there but when the map wants the S it's going to trigger the implicit conversion and here it is in practice so if your s is complicated to construct or whatever you can use this with result of and it will give the map it will get it in place will get the S when it wants it through conversion now again not particularly nice but if you need it you need it interesting note compilers are generally again with the most modern compilers I've tried both of these lines that one I've commented out is equivalent both of them optimize the same thing but go back in time to MSV C 17 and it's better optimizing the in line lambda than is at the function pointer it doesn't see through the function pointer so this is a case where lambdas are really good now in C++ 17 we got another way to put things in the map insert or assign so remember I said like operator square bracket works fine if you know the things already there but if you don't know it's there you get the default construct so what you need is probably insert or assign which just does the right thing in either case and this is now part of Maps interface augment and map and Friends I should say so but again the problem is it can't do explicit construction because look at its signature here it's taking one thing which is convertible to the map type it's not taking a parameter pack it's not doing in place construction like in place does it's just doing some kind of conversion and so if you want to use it with an explicit constructor and get in place construction properly you need to do something like this with a result of trick is everyone happy no good this is C++ so in case you're not keeping count I rewrote this slide so many times Maps interfaces screwed up we have not just not just five or more different ways of putting things in the map there are all different kinds of interface styles some of them take things to be converted some of them take parameter packs some of them take pairs which is the actual value type of the map and emerge even takes another map to put into a map so you know this is C++ as it is today and and you need to do what you need to do but I highly recommend watching Chandler's talk from from C++ now earlier this year where he describes a much better interface for maps maybe we'll have some nice things someday okay so there there is a there is a light at the end of this tunnel which is with C++ 20 quite often maybe the mapped type the thing that's the value in your map not the key might be an aggregate right now you're kind of screwed before C++ 20 because there is no way to construct the aggregate in place right but we're c-plus 20 we're getting parenthesized initialization of aggregates which means hopefully when your standard library implementers get around to it in place we'll work with aggregates and you can forward the arguments to construct your aggregate in place with presumably forward this tuple the same way as we've seen all right so recommendations for constructing things in place in the map are complicated and all of these things so remember about the piecewise construct argument of pair or just use a non-standard map with a better API all right now the picture gets better suck at Michael yes oh right I didn't cover tray in place is it in this slide try it yeah no it's not in this like it was in the last one yes I didn't mention it here because it pretty much I think I've covered all of the all of the different use cases try it so try in place twice like it says tries to emplace a thing and doesn't do anything if it's not there it's it's it's like insert or assign except it doesn't assign in sense but it's usage is very similar to in place which is why I didn't really call out a bunch of things about it yes right right trying place takes a parameter pack just like in place and so it can do construct yes key and parameter pack yes yes but it still can't construct an aggregate because you can't construct an aggregate yes oh right right oh I see I see I see right right right so try in place will effectively be a shorter version of this try it because try and place takes the parameter pack I see what you're saying yes that's that's true so it can be a shortcut for in places I think you're implying yeah yeah maybe there's maybe some light at the end of tunnel then let's talk in the brief time we have remaining about putting stuff into other things optional variant any the picture is in general better here because these things were born in the land where everything was already modern optional and variant and any all have this similar to the piecewise construct also they have a tag type constructor which takes this in-place argument and in the case of variant you get a choice of over whether you're constructing type wise or index wise and all of them perfectly forward their arguments and you get nice in-place construction so that's great in the case of optional you do need to be a little you know the note the second thing here is you know how we might normally declare and construct a thing in the code base but this but the third one is really the one you want to use I think in the second case again you're asking for an S right there so you're going to get the construct and then the move rather than the equivalent of in place by using the in place tag constructor when it comes to the optional assignment I recommend optional in place which is curiously named never think about it cuz that normally means construction here it means assignment or replacement is this very similar story again in the second case you're asking for an S right there so you're going to get a construction and then a move which you don't you know which isn't optimal so please use the in place T constructor for optional and the M place method and avoid explicit like I can like in every case avoid explicitly constructing the thing that you want to put in because if you're asking for explicit construction that's what you'll get yeah make optional yes as calling that the yes the third one here yes make optional is an alternative yes that's optional variant is somewhat similar so again we get it's fine to have an implicit constructor you know that's in that's fine but the explicit constructor again you want to avoid saying that and you can use and and there's the chance here that you will accidentally get a change in the code which will like do the thing that you didn't need or didn't want this will actually a bug you trying you're trying to construct an S not realizing that it's an explicit constructor and you're getting an int in this third case so my recommendation is again to use the in place constructors the in place tag constructors for variant do we have make variant I'm not sure there's no make variant thank you Michael so again the the in place has a type wise thing or an index wise thing I generally prefer to you the type wise there a alternative let's say that it's that variant the type wise alternative unless I have a variant with the type in multiple places which is fairly rare some people don't like that I don't mind it but it's it's pretty rare and for variant assignment again a very similar story to optional and again the implicit can be the explicit construction from int isn't going to be what you want on the last line here you now there's a semi-famous danger with variant which is that prior to c++ 20 it can do narrowing assignments and conversions to Bulls so you have a variant of a bull and the string you're trying to construct the string probably we need to give it hello but what you're actually doing is constructing a a bull because it says oh I know that's a constant car pointer and I can convert that to true so this prints index is zero thankfully in C++ 20 this is fixed because we we get P oh six oh eight a same variant converting constructor which prohibits narrowing conversions and conversions the bull so this this will work as intended in 20 and it's a very similar story for variant as it was with optional we get in place this time again in place takes the the tag type although the sorry the template parameter which is either the the type or the index so similar recommendations for variant always be explicit about the types you're creating or emplacing use the use the in place type or the in place index constructors use in place and in general I would avoid the assignment operator unless you're actually assigning variant of areand because you don't you just don't want to you know get implicit constructions and all that sort of thing you want to be sure about what you're getting so to sum up think about when you're writing code that create puts things into things think about what you're getting think about whether you're asking for an explicit construct right there that's usually not what you want to do if you want in-place construction in place construction as far as I know is always possible but often verbose and you know you need to know how rvo works and there take advantage of it you can always check small examples on compiler explorer know the interfaces of containers especially map because it's interface is really complicated by now and you know don't be afraid to use pushback if you're actually going to get a copy or a move you don't need the power of in-place back I would recommend just using pushback it's going to be easier for people to understand and it and it conveys confidence in your code and yeah watch jason's talk about initialize a list because it is problematic thank you very much do you think the it's on all right do you think that clang and other tools will help us find cases where we're just signing that has a copy to variant that could be replaced by in place at some point that's interesting yes that might very well be possible yeah I'd like to see that hi are there any limitations on the type of object being rvo like does it have to be trivial or can it be like a giant inscrutable type and it'll still be IV ode if I follow the rules as far as I know it can be giant inscrutable you probably want to check for named our vo the let's say for returning temporaries is much more reliable than returning a name thing in general temporaries almost always our vo no problem name stuff you definitely probably want to check is it possible when you have like the construct move move that the compiler could see through that and bypass it I don't know maybe but I don't think it would require some more standard wording I think because at the moment the compiler is so the compile is very limited than one that can do when it comes to changing the results of your code and copy a lesion on return is the only place in that standard where the compilers allowed to change the behavior of what you wrote and it's not allowed to do that for passing function arguments yet I have two comments so the first one is about the same converting constructor for variant yeah so I was just trying with the Eric phacelia about this yesterday and he shared an example where if you have something like variant of float an optional float you try to assign that with a double without the narrowing conversion it would actually happily construct your optional float because that's a user-defined conversion nice and so it's not quite so sane so that's the first covet Oh a sane uh yeah yeah so yeah all right so just don't use assignment with Varian the my point stands yeah the second the second comment is about in place it's more of a question maybe for so for example let's say you have a string inside of an optional right right and we want to do assignment into that string as opposed to like reconstructing one because let's say you have enough buffer allocated okay and place will actually destroy your existing string construct a new one mm-hmm all right so in order to forward the assignment you actually need to use the assignment not necessarily on the optional but yeah you could do start Oh a coaster to do the actual assignment through to the to the store thing for variant I think we actually should add a like explicit assign function because what you want is to be able to say like a sign to string some value right right and so rather than doing in place I think adding an assign function should be the thing to do for variance yeah that that sounds reasonable Thanks thank you [Applause]
Info
Channel: CppCon
Views: 11,039
Rating: undefined out of 5
Keywords: Ben Deane, 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: oTMSgI1XjF8
Channel Id: undefined
Length: 62min 16sec (3736 seconds)
Published: Tue Oct 08 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.