Youtube - Back to Basics Understanding Value Categories - Ben Saks - CppCon 2019

Back to Basics: Understanding Value Categories - Ben Saks - CppCon 2019

https://www.youtube.com/watch?v=XS2JddPq7GQ

Transcrição

[00:09] 

00:09 hey everyone my name's Ben Sachs for those of you who don't know me my company Sachs & Associates offers training in C and C++ for companies all over the world and I'm here to talk a
Pasted image 20240108224802.png

[00:21]

00:21 little bit about value categories in C and C++ and the first thing I want to mention is that value categories aren't language features not in the sense that the ability to do things like overload
Pasted image 20240108224812.png

[00:35]

00:35 operators is a language feature the value categories are part of how the compiler understands and interprets expressions in the language so understanding what these value

[00:50]

00:50 categories are and what they mean will help you understand how the compiler thinks which is helpful mostly both because it helps you decipher some error messages that might otherwise be

[01:03]

01:03 confusing and it also provides some insight into why certain features of the language like operators and references work the way that they do now originally there were only two value categories l

[01:18]

Pasted image 20240108224949.png

01:18 values and our values back when they were first introduced in C they were relatively straightforward and simple concepts then c++ came along it added classes const references and those

[01:35]

01:35 concepts needed to evolve to handle the new possibilities that these new language features offered and then we got along to modern C++ and r-value references were introduced and this

[01:49]

01:49 further complicated the situation the concepts had to evolve yet again and they also had to introduce new types of value categories to describe exactly what the language was doing in various

[02:02]

02:02 places so in this talk I'm going to kind of walk you through this evolutionary process we'll start from the early view of where these terms came from and I'll gradually refine the picture as we go so

[02:19]

02:19 there's a lot of material to cover I'm going to try and speak relatively quickly feel free to ask questions as I go so you may want to come up to the mic here too if you decide to do that but

[02:32]

Pasted image 20240108225044.png
Pasted image 20240108225054.png
02:32 you should be prepared for the possibility that I'll say that's coming up in a future slide ok so the terms l value and our value come from assignment expressions the idea is the L means left

[02:49]

02:49 B thing on the left hand side of an assignment statement the thing being assigned to has to be an L value so what's an L value it's any expression that refers to an object okay that begs

[03:03]

03:03 the question what's an object an object is a region of storage it's something that has a defined location in memory where it lives so let's take a look at a simple assignment statement here we have

[03:16]

03:16 an integer n we're assigning one to that n so here there are several expressions in that assignment statement and as an expression one is an expression and the whole thing n equals one is also an

[03:32]

Pasted image 20240108225140.png

03:32 expression so in here n is the sub expression that refers to an integer object it's an L value and that makes it the sort of thing that can be assigned to one is also a sub expression but it

[03:47]

03:47 is not an object it's just of an R value an R value is something that isn't an L value so anything that isn't an L value is an R value now I showed you the example I just showed you used

[04:04]

Pasted image 20240108225626.png

04:04 relatively simple expressions but those expressions can also be more complicated as in this example so here the left hand side is made up of several tokens it's X sub I plus one the right-hand side is

[04:16]

04:16 the absolute value of P arrow value both of those are sub expressions and the same rules apply the thing on the left hand side has to be an object that can be assigned to in this case it is

[04:31]

04:31 it's the I plus 1 member of the array X so it has to refer to an object the right-hand side can be either an L value or an R value we can assign from either L values or our values so any expression

[04:48]

04:48 will work on the right-hand side so why does this distinction exist why did the language designers introduce these two categories in the first place it lets the compiler be generate code more

[05:08]

05:08 efficiently than it would be forced to do if our values were required to occupy storage in memory so may give you an example going back to the N equals one assignment statement here so it's

[05:24]

Pasted image 20240111121932.png

05:24 possible that the compiler will choose to place that value one in data storage even though it doesn't have to put it there it may choose to put it there in as if it were an L value so in this case

[05:39]

Pasted image 20240111122041.png

 06:35

Pasted image 20240111122112.png

05:39 in the assembly language we would have a portion of the of the executable that is reserved for that value 1 in data space and then the assignment statement might look something like this where we move

[05:53]

05:53 the value from 1 into the location designated by n on the other hand the compiler might choose to do things like this there are many architectures that offer what are called immediate operands

[06:07]

06:07 where the value to be operated on doesn't have to be a separate data value it can actually be built into the instruction like this so in this case the value 1 appears directly in the move

[06:23]

06:23 expression so there's no data storage associated with that one it doesn't appear anywhere in data space it's a part of the code space built into one of the instructions if our values had to

[06:38]

06:38 occupy memory the compiler wouldn't be able to do this sort of thing so suppose that I wrote the assignment statement the other way around I said one equals n now we know we can't do

[06:52]

06:52 this but why can't we do this it's not because of pipes and is it an object of type int one is a literal which also has type in it they are the same pipe but it's not valid because the thing on the

[07:09]

Pasted image 20240111122147.png
Pasted image 20240111122216.png

07:09 left hand side has to be an L value and one isn't an L value it doesn't necessarily live at any defined place in memory so everything is either an every expression is either an L value or an R

[07:24]

07:24 value L values refer to objects that live in memory our values don't kind of this is true for built in objects of built-in types the picture is a little more complicated when we're talking

[07:39]

Marcador

Pasted image 20240111122258.png

07:39 about class types I'll explain that a little bit later on so what sorts of things are our values well literals as I've shown you so values like three 3.14159 character

[07:52]

07:52 literals like a these things don't have to occupy data storage it turns out that string literals like X Y Z Z Y here do have to occupy storage because these things have to behave like arrays you

[08:06]

08:06 need to be able to step through them with a pointer and in order to do that the thing has to live in a will to find location in memory so L values can appear on either side of an assignment

[08:20]

08:20 statement as we know the thing on the right doesn't have to be a literal value or something like that it can also be an object that we are assigning from and in this case we're using the value n as an

[08:36]

08:36 R value we're assigning from it and the language refers to this under a special term l value to our a value conversion which is sort of a the standards way of saying that we are we're taking we're

[08:52]

08:52 reading the value from n when we care about something being an L value we care about where that value will when we care about something being an r-value we care about what value it

[09:04]

09:04 holds so the concepts l value and our value come from assignment expressions but they apply in lots of other expressions as well so for example let's look at the plus operator by the way L

[09:23]

09:23 values and our values are one sort of restriction that can be applied to operators like this but there are others the things being added have to have types that are compatible for example so

[09:35]

09:35 the operands of the plus operator can be either L values or our values in either order whether I add X plus 2 or 2 plus X doesn't matter on the other hand what about the return value where does the

[09:53]

09:53 result M + n go it doesn't go in M it doesn't go in n it goes into a temporary location maybe a CPU register and that sort of temporary object is an R value it's not guaranteed to have memory

[10:10]

10:10 storage associated with it and so as such just like you can't assign to the literal one you also can't assign to the result of M plus 1 because that's an R value there's nowhere for the assignment

[10:25]

10:25 to assign to so another operator that we'll look at is the unary star operator so unary star yields an L value when we dereference a pointer star P is an L value it's something that can be

[10:43]

10:43 assigned to so here we have an example we have an array of integers we have a pointer pointing to one of the elements in that array we also have a character pointer that's initialized to null so

[10:56]

10:56 that assignment there star P equals 3 is valid because stars star P is an L value it lives at a particular location in memory star s is actually also an L value now I shouldn't do something like

[11:13]

11:13 this where I sign of value to it because it's pointing to null and that would give me undefined behavior but from the compilers perspective l value ness is

[11:24]

11:24 totally evaluated at compile time the assignment is problematic for other reasons but in terms of L value nice and our value Ness it's perfectly valid so here's how you think about data storage

[11:42]

11:42 because when the optimizer gets involved things change a little bit when we think about our values we don't think about them as occupying data storage as it turns out some of them might I showed

[11:56]

11:56 you that example of the immediate operands that are built into instructions earlier that worked well because I was using a small operand one but there's only a limited amount of

[12:08]

12:08 space in the instructions that can be given over to these immediate operands so if I had a very large value like 1 billion that I was trying to assign that might not fit in that space and in that

[12:23]

12:23 situation the compiler might fall back on giving that about that r-value storage in memory so it turns out some R values might be in memory but you're required to program as if they don't the

[12:38]

12:38 compiler has to have the freedom to not put on our value in memory we think of L values as occupying data storage now once the optimizer gets involved it might optimize away some of that storage

[12:53]

12:53 but it will only do that when you won't notice so you as the programmer are always allowed to program as if an L value has a location in memory and then the optimizer might just come along and

[13:07]

13:07 make things a little bit faster or more space efficient now on the other hand our values of class types we do think of those as occupying data storage so why do those have to be different well let's

[13:24]

13:24 take a look at this example where and we have a struct that's made up of two integers x and y by the way when we talk about the standard refers to objects of class types and when it talks

[13:37]

13:37 about a class type it means anything that the class a struct for a union so here we have an object of type s s1 initialized with the values one and four so what would it look like when the

[13:51]

13:51 compiler generates code to read from the Y data member of that object when in this case s1 is an L value of type s it depends on how it's being used in the expression and s we don't say that s1 is

[14:09]

14:09 an L value we say that when it's used in this expression it's being used as an L it's it is an L value so this assembly code is a little bit more complicated but what I want you to notice here is

[14:25]

14:25 that it the process of reading the Y data member it happens in two steps the first one is that it looks happy is that it takes the address of the s-1 object here and places that into a register and

[14:44]

14:44 then it uses that register as the base address of the s-1 object and performs a calculation using that base address to find the Y data member and then it reads from that location into another register

[15:01]

15:01 r2 and then once it has that value then it can go ahead and store that value into the location designated by I so there's this calculation going involved that involves going on that involves a

[15:15]

15:15 base address so what would it look like if we were dealing with an R value of type s like the return value of this function foo here so in this case the compiler still needs to go through a

[15:29]

15:29 similar process to access the data member Y of that return value which means that if it's going to perform that base plus offset how halation to get it why the return value

[15:43]

15:43 of foo has to have a base address when something has a base address it occupies a location in memory we think of it as occupying storage this is why our values of class types need to be treated a

[15:58]

15:58 little bit differently we'll refine this picture a little bit as we go on but this is just the first approximation so it turns out there are L values that actually can't appear on the left hand

[16:15]

16:15 side of an assignment statement this is one of the cases where once C++ got involved those concepts became more complicated because in C++ we can have constants like this array name here so

[16:31]

16:31 if I try and assign to it into one of the elements of name the elements of a constant are themselves Const I can't do that because it's a Const object it can't be modified

[16:46]

16:46 it still has a location in memory it's an L value but it's a non-modifiable l value and this term is actually used in the standard I didn't just make that up so this distinction between L values

[17:01]

17:01 non-modifiable l values and our values helps to give us a vocabulary for explaining certain differences in the language so for example here I have an enumeration constant called max time I

[17:16]

17:16 have a single enumeration unnamed that contains a single value called max that I expect to use in expressions as a constant integer this value max when it's used in an expression it behaves

[17:33]

17:33 like an order value so I can't assign to it like this I can't change the value of Max because Max is an r-value I also can't take its address I can't set up a pointer to point to the location where

[17:48]

17:48 that value max lives because there isn't necessarily a location that does that it's an r-value but if I make max a constant integer object like this then it becomes a non modifiable L value

[18:05]

18:05 I still can't assign to it because it's not the kind of thing we can have its value changed but I am allowed to take its address I can set up a pointer that points to the location at which max

[18:18]

18:18 lives in order to pass that or that pointer around max is an L value here so here's the this table gives you the summary l values are both addressable and assignable non-modifiable l values

[18:34]

18:34 are addressable but not assignable class or non class or values are not addressable and not assignable again it's a little bit more complicated for class R values as I explained so when we

[18:51]

18:51 think about these B this distinction helps us explain the behaviors of references so for anyone not aware references behave very much like pointers in C++ when I create a

[19:08]

19:08 reference a reference to an end RI here and I bind it to an end on that essentially sets up RI as an alias for I at that point any time I go on to use RI in an expression after that the the code

[19:28]

19:28 behaves as if I had written I instead so references the standard takes great pains to say that references and pointers are different things they're not necessarily implemented the same way

[19:44]

19:44 but in practice you can you almost always think of them as being the same moment and a lot of code that you that uses references could also be rewritten to use a constant pointer instead not a

[19:59]

19:59 not a pointer to Const not something that points to something that can't be modified but a pointer that itself can't to point to anything else because just like because a reference once it's bound

[20:13]

20:13 to a value like I it is always bound to I there's no way to rebind it to something else so the closest analogy with pointers is a constant pointer that

[20:24]

20:24 always points to the same thing and then anywhere that we would have used our eye with a reference we can use star CPI as a pointer and so just like star CPI yielded an l-value references are also L

[20:40]

20:40 values so why does why did the language choose to introduce references if they complicated things like this the real reason that references are in C++ is to make overloaded operators work the way

[20:55]

20:55 that you would like one of the design philosophies in C and C++ is that built-in types and class types should behave very much the same you should be able to write a class type that looks

[21:10]

21:10 like a built-in type and if we don't have references it's really hard to write overloaded operators that give us that behavior so here is an example we have an enumeration type month that

[21:24]

21:24 represents the different months in the year in see in the original C enumerations were basically just integers they were it was a paper thin disguise over an int so I would have

[21:38]

21:38 been allowed to write code like this where I loop through all of the months and I using a variable of type m and I you do plus plus M that compiles and runs in C it doesn't work in C++ because

[21:56]

21:56 enumerations are treated more like distinct types in C++ so if I want to make this work I would need to overload the plus plus operator for month so let's try and do that without references

[22:10]

22:10 and I hope you'll see why references are so important so here we're writing the plus plus operator as passed by value we just pass in the you of a month we go ahead and add one

[22:23]

22:23 to that month value we have to use a static past to turn it into an integer or we need it will convert to an integer automatically but then we need to convert it back into month to update the

[22:37]

22:37 value of X except that updating the value of X doesn't really do what we want it because I can write plus plus M but it doesn't actually change the value in M M was passed to X by value so X

[22:54]

22:54 gets incremented but M doesn't so this doesn't give us the behavior that we want even worse because it's in this situation I'd be allowed to apply the plus plus operator to an hour value like

[23:08]

23:08 the April value here that's not something you can do with the built-in operators if you have an hour value of type int like 42 you're not allowed to apply the plus plus to it so okay we

[23:24]

23:24 passed by now you didn't work let's try pass by pointer instead so here we would be taking the value the month that we want to point to in by pointer turns out you can't even write this it just

[23:39]

23:39 doesn't compile overloaded operators in C++ can't have parameters of pointer types but let's suppose it did compile for a moment it still wouldn't look the way that we want it to look it doesn't

[23:53]

23:53 behave like the built-in types because plus plus M isn't what you would write the thing that you would have to pass off as the argument to this function would be the address of M so whereas you

[24:08]

24:08 would write plus plus I to increment an int you'd have to write plus plus ampersand m to increment a month and that just ruins the whole thing it destroys the illusion that we're trying

[24:21]

24:21 to create that built-in types and class and user-defined types like month behave the same way so we really need a references in this case we need something that

[24:34]

24:34 Caves like a pointer but doesn't force us to take the address of the thing when we're passing it in so here we passing the value of the of the month by reference we can update the value inside

[24:48]

24:48 there and now as a result + + M actually changes the value that it's apply the value of the argument that it's applied to it works the way that we want and as a bonus plus plus in this case will only

[25:02]

25:02 work on L values references won't bind to our values because our values don't have a well-defined location in memory there's not necessarily a place where April lives that we can update its value

[25:19]

25:19 this way yeah by the way if I were really writing a + + operator it doesn't return void it returns a reference to the thing that's incremented I didn't want to distract the discussion before

[25:33]

25:33 this point with that but this is what it really looked like ok so we can also have parameters that are referenced to Const and references to cost will bind to objects that are either Const or non

[25:50]

25:50 cost references to non Const objects will only bind to modifiable objects non constant Jax so so in this case this function would accept any T object whether it was constant or non constant

[26:12]

26:12 inside the function f it would be it would be a reference to Const and it would behave like a non-modifiable l value something that has an address but I can't change the value there so this

[26:28]

26:28 function winds up behaving very much as if I had written the same function using passed by value instead in the sense that if I use if I write F and I whether I'm passing by a value or by reference

[26:44]

26:44 to Const I know that the call to F isn't going to change the value of x because if I pass by value the the function only sees a copy of X it doesn't see the value of X itself you can't change X if

[27:02]

27:02 I pass by reference to Const the function sees a non-modifiable l value of type reference that is bound to X it can't change X because X is treated as Const in that situation so why would I

[27:19]

27:19 want to make a distinct what I want to do these things pass by reference the cons instead of pass by value passing by reference to Const might be more efficient making a copy of a value is

[27:31]

27:31 required when you pass by value if you're talking about a class type that's very large that copy could be a very expensive thing to do whereas binding a reference to something is very cheap and

[27:46]

27:46 the cost doesn't go up as the object gets bigger so as I mentioned before a pointer to a tee object can only point to an L value of type T and in the same way a reference to non cons T will only

[28:04]

28:04 bind to an L value of type T so I can't take the address of 3 and assign it to a pointer to an integer and in the same way I can't bind a reference to an integer to the literal three those both

[28:20]

28:20 neither of those works because three is an r-value for the same reason I can't make I can't set up a pointer to to an object that isn't of the right type for the pointer to point to so I can't make

[28:36]

28:36 a pointer to a double that actually points to an integer even though I is an ml value here it's an it's an L value of the wrong type so I can't take the address of I and put it in a pointer I

[28:50]

28:50 also can't bind a reference to a double to I because I as a vid is an int well sort of there's an exception to that rule when we get into reference two constant objects it turns out that a

[29:04]

29:04 reference to a compte can bind to an expression X that's not an L value of type t as long as there's a conversion from whatever type x has into the type t that the reference wants to bind to the

[29:24]

29:24 way that this works is that the compiler creates a temporary object that holds the value of X converted into a T object and that temporary object that was created that's what the reference winds

[29:40]

29:40 up being bound to that temporary object has some sort of location in memory that there but the reference can refer to so here's an example I'm allowed to write a reference to a constant double Rd and

[29:55]

29:55 bind that to a3 and the way that it would work is three is of type end so the compiler would first convert that three into a double with the same value it would put that converted value into a

[30:11]

30:11 temporary object of type double that holds the result of that conversion and then it would bind Rd to that temporary object that temporary object would last as long as the reference Rd lasts when

[30:26]

30:26 Rd is destroyed the temporary is also destroyed so why is this so spec but why do things in this special way why does reference the Const have to behave this way and it's done

[30:41]

30:41 so that passed by reference to Const is a viable replacement for passed by value we want to be able to pass by reference to Const instead of passing by a value because it might be cheaper than passing

[30:57]

30:57 by value but think about what happens if I pass by value so here I have a function f that takes a long double by value I can pass it out a long double value like X there in which case

[31:14]

31:14 we just generate a copy of X passed that off as the argument to the function on the other hand I can also pass in values that aren't long doubles like this integer one here one the literal one is

[31:32]

31:32 of type int when the compiler generates the code for that it will convert the one into a long double and pass that as the argument to the function pass that converted value as the argument to the

[31:44]

31:44 function so in order to make reference to Const work the same way it's important that I be able to pass not just a long double to a function that expects to take a reference to a

[31:58]

31:58 constant long double but also something like an integer in this case an integer that is an hour value in which case we create this temporary object and bind the reference to that and then and it's

[32:14]

32:14 perfectly safe to do that to bind the reference to that temporary because it's non-modifiable there's no danger that someone will call this function expecting to get back a modified version

[32:28]

32:28 of one because the thing has to be treated as Const inside F so either way the function calls essentially had equivalent behavior from the callers perspective so I talked before about how

[32:46]

32:46 normally we don't want we think of our values of built-in types as not occupying storage our values of class types do occupy storage this temporary object that we've just created as part

[33:02]

33:02 of doing this reference binding that also occupies storage it has to because the reference has to have some a place in memory to refer to and this works even for built-in types like long double

[33:17]

33:17 it's not true just for class types so this is another way that you can have our values that have locations in memory that occupy data storage the way that class our values of class

[33:30]

33:30 types do it turns out in modern C++ there are two kinds of our values and they have special names there are pure Val our values or P our values which don't occupy data storage conceptually

[33:47]

33:47 and then there are what are called expiring values or X values which do occupy data storage both of these are considered to be types of our values therein so when I refer to our values

[34:02]

34:02 going forward I could be talking about either a P R value or an X value it turns out as the programmer you don't need to worry that much about the distinction of what an art or what an x

[34:18]

34:18 value is it's mostly in the standard to describe the behavior of the language so that compiler authors know what needs to be done you as the programmer don't usually think in terms of X values but I

[34:32]

34:32 wanted to make sure that you were aware that you were had some understanding of the term because I know that a lot of people are familiar with it but don't necessarily know what it means so when

[34:45]

34:45 the temporary is created as part of binding this reference to a constant object to an R value it's created through what's called a temporary materialization conversion it's a

[35:00]

35:00 conversion that turns a P R value that doesn't have a location into an x value that does have a location so that's what's going on there so suppose that I want let's revisit the idea of

[35:16]

35:16 overloading operators and let's look at how would we create an overloaded plus operator for a string type that does that concatenates two strings together that behaves in a way that looks very

[35:32]

35:32 much like the way that the built-in plus operator behaves for objects like integers so here's just a rudimentary version of the string class it has a copy constructor

[35:45]

35:45 it has a converting constructor that allows us to treat string literals as as string objects and it also has an assignment operator a copy assignment operator to be specific and then we have

[36:00]

36:00 our plus operator that will concatenate two strings together so the plus operator takes its arguments by reference to Const which means that they will bind to either L values or R values

[36:15]

36:15 of the appropriate type which means that I can write something like this where I have two strings s and T and then I I can add those together so that I say s plus the string literal containing the

[36:33]

36:33 comma plus T and the compiler will interpret it like this and we'll see that that string literal can be treated as a pointer to constant character we have a converting constructor from

[36:50]

36:50 pointer to constant character into a string so it will first apply the converting constructor there so that we have all operands of type string in that case and the result of doing that

[37:03]

37:03 conversion is that is that the string that's created is an r-value it's a temporary object and whereas SNP in this expression behave like L values on the other hand the function returns the

[37:20]

37:20 result by value so the result of the func of calling that function is an r-value you can't take it address and set up a pointer that will point to it the way that you could for S or T

[37:34]

37:34 individually so up until now I've been referring to just references that's the way that these things work that reference is referred to in C++ oh three all there were were

[37:52]

37:52 references in C++ eleven and Beyond we now have two different kinds of references we have L value references which are the references that existed before in C++ oh three and we also now

[38:08]

38:08 have our value references which are a new feature introduced in modern C++ so L value references behave the same way that that references did before our value references were introduced so l

[38:25]

38:25 value references are declared with the single with a single ampersand operator our value references are declared with a double ampersand operator so here our eye is an R value reference to an int

[38:44]

38:44 unsurprisingly an R value reference and bind to an R value like 10 there so you are value references can appear as in lots of different places but they're typically used as function parameters

[38:59]

38:59 and function return pipes just like L value references are traditionally we usually don't create reference objects on the stack and pass them around where they're usually used as function

[39:14]

39:14 parameters and return pipes and you can have our value references to Const like this it's usually not a particularly useful thing to do because as you'll see our value references are in the language

[39:30]

39:30 to implement something called move semantics which usually expects the thing to be treated as mod the thing being referred to to be treated as modifiable and an R value to reference

[39:43]

39:43 to comes refers to a non-modifiable object so our value references will only bind to our values even an R value reference const will not bind to an l-value it

[39:57]

39:57 will only bind you to an r-value so here if i have an integer n initialized with the value 10 i can't bind on our value reference to n because n is an L value it's not an R value even if I'm trying

[40:14]

40:14 to bind a reference to a an R value reference the cons to it it doesn't work when I bind the R value reference to an R value that triggers a temporary materialization conversion that thing

[40:32]

40:32 that turns a PR value into an x value the same way that binding an L value reference to Const to an R value triggered a temporary materialization conversion similar behavior in both in

[40:47]

40:47 both cases so as I mentioned r-value references are in the language to implement implement move semantics they provide a mechanism by which in some situations we don't want to copy an

[41:07]

41:07 object it's cheaper if we can instead of leaving if we want to take the value from one object and put it into an into another object if we need to preserve the value in the original object then we

[41:22]

41:22 need to make a complete copy of that object into the new object but if we don't care about preserving the value of the original object for example if it's only a temporary object like an R value

[41:37]

41:37 then it's then we might be able to implement the transfer of value more efficiently by moving that value just stealing the contents of the thing that we're assigning from and placing them in

[41:52]

41:52 the thing that we're assigning to that's a move operation so this is in this case the class string has both copy operations that take their parameters by reference to Const and

[42:07]

42:07 move operations that take their parameters by r-value reference to non Const if they took them for the parameters by our value reference to Const they would

[42:17]

42:17 still be binding to our values but those are values would be treated as non-modifiable and the purpose of being able to do a move operation is that we want to be able to destroy the thing

[42:30]

42:30 that we are moving from because that might provide us with a more efficient way of getting the value that we want into the object that we want so here are some objects three objects of type

[42:43]

42:43 string s1 s2 and s3 if I assign from s2 to s1 that that performs copy assignment s2 in this expression designates an L value it it's not expiring so the value has to be preserved which means it has

[43:04]

43:04 to be copied it can't be moved from on the other hand if I assign from an R value like s2 plus s3 the result of that plus operator as I showed you a moment ago that will perform move assignment

[43:20]

43:20 it's safe to move from the result of s2 plus s3 because that's a temporary object it's going to die as soon as we reach the end of that statement so it's perfectly safe to move the value out of

[43:36]

43:36 s2 plus s 3 into s 1 because because s2 plus s3 is going away it's sort of I like to think of this as C++ providing a way for s1 to say hey are you gonna eat that because if you're not I'll take it

[43:58]

43:58 so when you bind an R value reference to an R value it performs one of those temporary materialization conversions it creates an X value and then an expiring value but let's look at the

[44:13]

44:13 code inside the move assignment operator here that r-value references a parameter to the function called other there now let's suppose somewhere inside this operator I wanted to create a

[44:29]

44:29 string temp using the value of other with this perform copy assignment and copy construction or move construction it turns out it performs copy construction it's not treated like an

[44:44]

44:44 r-value that I could move from it's treated like an L value because from the perspective of the caller the r-value the thing behaved as an r-value it was about to die at the end of the statement

[45:01]

45:01 it was safe to move from but inside the function this function could be hundreds of lines long I hope it isn't but it could be as long as I want so from the perspective of people inside this

[45:16]

45:16 function the object other looks like it has a fairly long life it lives as long as the function does so it needs to be treated as something that doesn't expire like an L value and the general rule

[45:30]

45:30 here is pretty simple if it has a name it's an L value it doesn't have a name it's an R value it might be an R value I should say not necessarily unnamed things aren't necessarily our values

[45:46]

45:46 named things are are almost always L values so there are some situations though where it does make sense to move from an L value so here the most common example is swap here if I were to before

[46:02]

46:02 the advent of our value references a swap function would typically be written something like this some I take in two values a and B I copy the value from a into a temporary location I over write a

[46:18]

46:18 with B and then I copy the value that was in a now in the temporary location into B if I even if T has moved has a move constructor and a move assign and operator in this case but if it's

[46:37]

46:37 written like this the compiler will generate code that performs copy construction and copy assignment in this case because the compiler doesn't see a just focus on the first case here where

[46:54]

46:54 we're creating the object temp from a hey doesn't look like it's expiring to the compiler it lasts as long as the function does but as the programmer we can tell the very next line over writes

[47:10]

47:10 the value of a there's no need to preserve it it would be safe to perform a destructive copy aka a move when we're creating from a to create temp so it's safe to move from an

[47:28]

47:28 L value if it's expiring but the but the compiler can't always tell when an L value is about to expire in some cases the programmer has knowledge but the compiler can't real that the compiler

[47:44]

47:44 can't easily discern so how can we tell the compiler that an L value is about to expire we need to convert it into an X value an expiring value in other specifically we need to convert it into

[48:01]

48:01 an unnamed r value reference something that designates an object but it designates a temporary object that's about to expire and can be safely moved from and that's what the standard move

[48:16]

48:16 function does here when I call move on an object that that perfective li performs a conversion from from from that object from L value into X value now what I'm showing you here is a

[48:33]

48:33 somewhat simplified version of the function move if you go look at the way that it's defined in the standard it actually looks more complicated than this but I want you to focus on the fact

[48:44]

48:44 that it returns an R value referenced to a tee return values don't have names so the result of a move is an unnamed r-value reference an X value again as a programmer we don't often think about X

[49:03]

49:03 values because we're usually thinking about something as either a long-lived value like an L value or a short-lived value like an R value but but this term x value is mainly there for compiler

[49:20]

49:20 writers to describe this slightly in-between state that objects can be in so the actual way that it's described in the standard looks like this is that there are expressions and expressions

[49:34]

49:34 break down into two categories there are our value P values and generalized L values GL values and then what I've been called what I've usually been calling an L value up to this point is one kind of

[49:51]

49:51 GL value an L value only behaves like an L value it has a location in memory a PR value a pure R value always behaves like an R value we have to treat it as if it doesn't have a location memory an x

[50:07]

50:07 value behaves like both it behaves both like an L value and an R value and so that's what's going on in this picture that's the the final truth that the fix the past and 'red has at the moment that

[50:27]

50:27 brings me to the end of the talk so I'm happy to take questions at this point but otherwise I hope you find it useful good luck in your programming [Applause]

[50:57]

50:57 I'm sorry when did you know L values what implicit implicitly become X values what was that the question okay when did and when do L values implicitly become X values essentially in return

[51:13]

51:13 statements it's a little bit estándar uses more complicated terminology than that but almost always when you think of an L value that B that is being converted into an X value and you don't

[51:28]

51:28 need to explicitly move from it it's usually in a return statement yeah who do you want to use the microphone okay the question is is it was like her did I say that a string

[51:59]

51:59 literal was an L value and did I really mean to say that yes I did the string literals have to have some location in memory because so a string literal is really an array underneath an

[52:17]

52:17 array has to have elements laid out in memory in some kind of sequential order so that you can do things like step through it with a pointer like we often do with Springwood all literals say if

[52:30]

52:30 you wanted to take the length of it with sterlin what says that you can't assign to to a string literal you can't really it's because we often think about string literals as being pointers to constant

[52:55]

52:55 characters but that's not actually their real type their real type is array of constant characters and you can't assign to an array in as a in a blanket form so if you tried to put

[53:10]

53:10 a string literal on the left hand side of an assignment statement you'd be assigning to a complete array and the language just doesn't let you do that okay I'm happy to if if you're if you

[53:32]

53:32 don't want to use the microphone I'm happy to have you come up and ask me questions more individually as I said other what otherwise though as I said good luck in your coding endeavors

[53:41]

53:41 endeavors and I hope you find this stuff useful [Applause]