1 00:00:05,240 --> 00:00:09,320 In this video, we'll look at what it means for Lambda Expression to be stateless. 2 00:00:09,980 --> 00:00:14,270 You might be surprised to hear that you're already familiar with stateless lambda expressions. 3 00:00:14,450 --> 00:00:20,240 In the last video we looked at the structure and syntax of a lambda and said that a lambda capture list 4 00:00:20,240 --> 00:00:24,410 allows it to capture the context or closure in which it executes. 5 00:00:24,410 --> 00:00:26,480 But what if the captured list is empty? 6 00:00:26,480 --> 00:00:32,299 An empty capsule list means that the expression captures no information from its environment and only 7 00:00:32,299 --> 00:00:36,470 knows about the information passed to it via the function parameter list. 8 00:00:36,500 --> 00:00:39,680 This is what's known as a stateless lambda expression. 9 00:00:39,770 --> 00:00:41,900 Now let's take a look at some examples. 10 00:00:42,680 --> 00:00:45,890 In this example, we have two stateless lambda expressions. 11 00:00:46,100 --> 00:00:49,280 We can tell that they're stateless from their empty capture lists. 12 00:00:49,610 --> 00:00:54,140 The first expression takes no parameters and simply displays high one executed. 13 00:00:54,350 --> 00:00:58,250 Everything it needs to execute is contained within its function body. 14 00:00:58,430 --> 00:01:04,489 The second expression takes a single integer X as a parameter and displays the value of X when it's 15 00:01:04,489 --> 00:01:05,000 called. 16 00:01:05,030 --> 00:01:10,790 We can see that this lambda is instantiated and called with a value of 100 as its x parameter. 17 00:01:11,150 --> 00:01:15,560 But we can also see that another integer x has been defined above. 18 00:01:15,800 --> 00:01:18,860 Can you tell which x value the lambda will display? 19 00:01:19,460 --> 00:01:25,200 Remember this lambda expression is stateless and so it captures no information from its environment. 20 00:01:25,220 --> 00:01:31,220 This means that the lambda has no knowledge of the integer x defined above and only knows about the 21 00:01:31,220 --> 00:01:35,840 value that was assigned to its parameter X when it was instantiated and called. 22 00:01:36,050 --> 00:01:41,600 Since the parameter X was assigned a value of 100, the lambda will display 100. 23 00:01:42,720 --> 00:01:49,350 In this example, the lambda expression called Sum takes an integer array and its length as parameters 24 00:01:49,350 --> 00:01:52,920 and returns the sum of the integers contained within the array. 25 00:01:53,190 --> 00:01:59,220 We can see that the lambda parameter names are the same as the integer array and length defined above. 26 00:01:59,580 --> 00:02:05,100 But again, we know from the empty capsule list that this lambda expression is stateless, so it has 27 00:02:05,100 --> 00:02:08,340 no knowledge of the array defined above or its length. 28 00:02:08,550 --> 00:02:13,830 The only way for the lambda to compute the sum of the integers contained within the array is if the 29 00:02:13,830 --> 00:02:17,310 array and its length are passed to the lambda as parameters. 30 00:02:17,670 --> 00:02:22,710 We can see this being done when the lambda is called, and so the lambda will compute and return the 31 00:02:22,710 --> 00:02:23,820 sum 60. 32 00:02:25,350 --> 00:02:29,850 So far we've only seen lambda expressions that use value parameters. 33 00:02:29,850 --> 00:02:33,330 But you're probably wondering, could they also be reference parameters? 34 00:02:33,360 --> 00:02:34,500 Absolutely. 35 00:02:34,770 --> 00:02:40,110 Remember that by passing a parameter, by reference to a function or lambda expression, you're passing 36 00:02:40,110 --> 00:02:42,360 the location of the actual parameter. 37 00:02:42,360 --> 00:02:47,400 So the formal parameter and the function body is an alias to the actual parameter. 38 00:02:47,430 --> 00:02:50,760 Unlike passing by value, no copy will be made. 39 00:02:51,180 --> 00:02:54,750 When you change the formal parameter, you're changing the actual parameter. 40 00:02:54,780 --> 00:02:56,460 Let's take a look at an example. 41 00:02:57,240 --> 00:03:03,570 In this example, we have the lambda expression called bonus that takes as its parameters two test scores 42 00:03:03,570 --> 00:03:05,730 and a number of points to increase them by. 43 00:03:05,760 --> 00:03:12,540 You'll notice that both score one and score two are reference parameters, so any changes made to them 44 00:03:12,540 --> 00:03:16,020 within the Lambda body will change the parameters they're referencing. 45 00:03:16,500 --> 00:03:21,720 This means that there's no need to return the increased scores, since the parameters they're referencing 46 00:03:21,720 --> 00:03:23,430 will already have been updated. 47 00:03:23,670 --> 00:03:26,140 We can see that bonus is called and passed. 48 00:03:26,160 --> 00:03:33,000 Test score one, test score two and five as the number of bonus points to be awarded to each test score. 49 00:03:33,030 --> 00:03:39,000 As the Lambda Body executes, score one and score two are increased by five points. 50 00:03:39,360 --> 00:03:44,700 But these are just aliases for the parameters test score one and test score two. 51 00:03:44,790 --> 00:03:49,860 So it's actually test score one and test score two that are being increased by five points. 52 00:03:50,070 --> 00:03:56,040 Once the Lambda has finished executing test score one and test score two are displayed with their updated 53 00:03:56,040 --> 00:03:56,820 values. 54 00:03:57,660 --> 00:04:03,870 You may remember that in C++ it's possible to use pointer parameters to achieve past by reference. 55 00:04:04,350 --> 00:04:11,010 So naturally it follows that lambda expressions can also use pointers as parameters to use pointer parameters 56 00:04:11,010 --> 00:04:11,700 properly. 57 00:04:11,700 --> 00:04:16,620 We need to remember that the pointer parameter is pointing to the address of the parameter that's being 58 00:04:16,620 --> 00:04:17,339 passed. 59 00:04:17,339 --> 00:04:23,670 So when calling the lambda, we need to use the referencing or address of operator to pass the address 60 00:04:23,670 --> 00:04:24,660 of the parameter. 61 00:04:25,050 --> 00:04:30,930 Additionally, because the Lambda Pointer parameter is pointing to the address of the actual parameter, 62 00:04:31,230 --> 00:04:36,960 if we want to access or modify the parameter stored at that address, we need to use the DX referencing 63 00:04:36,960 --> 00:04:37,680 operator. 64 00:04:37,860 --> 00:04:43,080 All of this was covered in detail in section 12 of the course, but I think it's important that, you 65 00:04:43,080 --> 00:04:46,020 know, it can also be implemented with lambda expressions. 66 00:04:46,110 --> 00:04:49,920 Now let's quickly take a look at that example with the bonus lambda expression. 67 00:04:49,920 --> 00:04:51,690 But this time using pointers. 68 00:04:52,080 --> 00:04:54,530 Notice the differences from the last example. 69 00:04:54,540 --> 00:04:58,380 The parameters score one and score two are now pointer parameters. 70 00:04:58,740 --> 00:05:04,680 So when bonus is called, the referencing or address of operator is used to pass the addresses of test 71 00:05:04,680 --> 00:05:06,870 score one and test score two. 72 00:05:07,170 --> 00:05:13,140 We can also see that the DX referencing operator is used to modify the values that score one and score 73 00:05:13,140 --> 00:05:16,020 to a point to within the Lambda Body. 74 00:05:16,200 --> 00:05:21,480 Now that we've seen how Lambda expressions can use values, references and pointers as parameters for 75 00:05:21,480 --> 00:05:27,960 different data types, you may be asking yourself Can the same be applied to data structures and objects? 76 00:05:27,960 --> 00:05:29,580 And the answer is, of course. 77 00:05:29,880 --> 00:05:35,760 Let's take a look at how we can use vectors as reference parameters to pass multiple test scores to 78 00:05:35,760 --> 00:05:37,500 the Lambda Expression bonus. 79 00:05:38,130 --> 00:05:44,190 Here we see that the Lambda Expression bonus takes as its parameters a reference to an integer vector 80 00:05:44,190 --> 00:05:50,340 containing the scores to be awarded bonus points and an integer number of bonus points to be awarded 81 00:05:50,700 --> 00:05:52,140 within the Lambda body. 82 00:05:52,140 --> 00:05:57,510 A range based for loop is used to iterate over each individual score contained within the vector. 83 00:05:57,720 --> 00:06:03,210 You'll notice that the referencing operator is used in front of score in the range declaration of the 84 00:06:03,210 --> 00:06:03,930 for loop. 85 00:06:04,350 --> 00:06:08,610 This is because we want to modify the scores contained within the vector. 86 00:06:08,910 --> 00:06:14,100 If we didn't use the referencing operator, no change is made within the body of the lambda would carry 87 00:06:14,100 --> 00:06:15,030 outside of it. 88 00:06:15,300 --> 00:06:21,000 We can see that bonus is called and pass the integer vector test scores and five is the number of bonus 89 00:06:21,000 --> 00:06:23,070 points to be awarded to each test score. 90 00:06:23,670 --> 00:06:28,170 Once the lambda has finished executing, the updated test scores are displayed. 91 00:06:28,440 --> 00:06:29,610 Pretty simple, right? 92 00:06:29,610 --> 00:06:34,290 But what if the vector test scores contain floating point values instead of integers? 93 00:06:34,590 --> 00:06:40,380 Could we modify this lambda expression so that it could take both integer and floating point vectors 94 00:06:40,380 --> 00:06:42,270 as its scores parameter? 95 00:06:42,270 --> 00:06:47,910 Moreover, is there a way to write any lambda expression to accept any data type for a single parameter? 96 00:06:48,000 --> 00:06:48,930 Let's find out. 97 00:06:49,840 --> 00:06:55,720 Here we have two variables, number one and number two, which are of type integer and float respectively. 98 00:06:55,720 --> 00:06:59,740 The lambda expression L takes a parameter X and displays its value. 99 00:07:00,280 --> 00:07:06,100 What type specifiers would we need to use for parameter X so that the lambda could display both number 100 00:07:06,100 --> 00:07:07,960 one and number two when called? 101 00:07:08,960 --> 00:07:14,330 Just like we use the key word auto to tell the compiler to deduce the type of the lambda expression. 102 00:07:14,660 --> 00:07:19,130 We can also use it to tell the compiler to deduce the type of the parameter. 103 00:07:19,160 --> 00:07:26,210 This is a very powerful concept in C++ and it's commonly used with lambda expressions to increase robustness, 104 00:07:26,210 --> 00:07:27,930 performance and usability. 105 00:07:27,950 --> 00:07:31,340 Let's see how we can implement this concept with our last example. 106 00:07:32,380 --> 00:07:38,890 We can see now that by changing the type specified of the scores parameter to auto, the lambda expression 107 00:07:38,890 --> 00:07:41,930 can be passed both integer and floating point vectors. 108 00:07:41,950 --> 00:07:48,220 In fact, it can be passed in any container that can be iterated over so long as the logic of the lambda 109 00:07:48,220 --> 00:07:50,980 body can be applied to the contents of the container. 110 00:07:51,010 --> 00:07:56,710 You might also notice that the type specified for the for loop range declaration has changed to auto 111 00:07:56,710 --> 00:07:57,520 as well. 112 00:07:57,550 --> 00:08:02,710 This is necessary if we want the for loop to be able to iterate over whatever data type is contained 113 00:08:02,710 --> 00:08:04,270 within the scores container. 114 00:08:04,900 --> 00:08:10,600 You can see from this example how useful the auto keyword is when implementing lambda expressions. 115 00:08:10,600 --> 00:08:15,700 So going forward will be using the regularly as lambda parameter type specifiers. 116 00:08:15,730 --> 00:08:19,090 Just remember, auto is not an actual type. 117 00:08:19,180 --> 00:08:22,810 It's an instruction telling the compiler to deduce the actual type. 118 00:08:23,610 --> 00:08:29,040 One of the best things about Lambda expressions is their ability to be passed into functions as parameters. 119 00:08:29,940 --> 00:08:31,560 Is C++ 14. 120 00:08:31,590 --> 00:08:33,539 There are two ways in which we can do this. 121 00:08:33,570 --> 00:08:39,210 The first is by passing a lambda expression as a function object using the standard libraries functional 122 00:08:39,210 --> 00:08:39,750 header. 123 00:08:40,500 --> 00:08:46,770 We could see this done in the first example as the function foo takes as its parameter the function 124 00:08:46,770 --> 00:08:53,340 object l the void type specify a represents the return type of the function object and the integer type. 125 00:08:53,340 --> 00:08:56,670 Specify R represents the function object's parameter type. 126 00:08:56,760 --> 00:09:03,060 In this case, the function L takes as its parameter an integer, for example ten and returns nothing. 127 00:09:03,690 --> 00:09:08,670 The second way of passing a lambda expression to a function is as a function pointer. 128 00:09:08,940 --> 00:09:14,220 In the second example, the function foo takes as its parameter a pointer to the function l. 129 00:09:14,990 --> 00:09:21,190 Again, the void type specified represents the return type of the function and the integer type specify 130 00:09:21,190 --> 00:09:23,510 a represents the functions parameter type. 131 00:09:23,720 --> 00:09:29,570 In C++ 20, we could do away with the restrictiveness of having to declare the return and parameter 132 00:09:29,570 --> 00:09:35,750 types, and we can simply use the auto keyword to tell the compiler to deduce both the return type and 133 00:09:35,750 --> 00:09:38,150 the parameter type of the pass lambda expression. 134 00:09:39,710 --> 00:09:46,010 In addition to using lambda expressions as function parameters, we can also have functions return lambda 135 00:09:46,010 --> 00:09:50,240 expressions similar to how we can pass lambda expressions to functions. 136 00:09:50,240 --> 00:09:56,390 We can return them as either function objects, function pointers or by using the auto keyword to instruct 137 00:09:56,390 --> 00:09:58,610 the compiler to deduce the return type. 138 00:09:58,820 --> 00:10:04,030 You'll notice that the syntax of the second example which returns a function pointer is a little strange. 139 00:10:04,040 --> 00:10:05,300 This is old style. 140 00:10:05,300 --> 00:10:10,610 See syntax that has survived through generations for backward compatibility, but you shouldn't give 141 00:10:10,610 --> 00:10:13,220 it too much attention in modern C++. 142 00:10:13,220 --> 00:10:18,290 If you want to return a lambda expression from a function, you'll most likely return it as either a 143 00:10:18,290 --> 00:10:20,870 function object or by using the auto keyword. 144 00:10:21,230 --> 00:10:24,620 So here you see three different ways to achieve the same result. 145 00:10:24,800 --> 00:10:28,190 What would it look like to actually call and use these functions? 146 00:10:28,490 --> 00:10:29,570 It's simple. 147 00:10:29,600 --> 00:10:32,780 All three versions use the exact same way. 148 00:10:33,020 --> 00:10:36,680 First we call Foo and assign its return value to a variable. 149 00:10:36,680 --> 00:10:41,570 Then we use the variable to execute the function and pass any parameters into it that it needs. 150 00:10:42,110 --> 00:10:45,500 In this case, we're passing a ten and the ten is displayed as a result. 151 00:10:45,740 --> 00:10:51,200 Examples of why you might want to return a lambda from a function or best explained using stateful lambda 152 00:10:51,200 --> 00:10:52,010 expressions. 153 00:10:52,010 --> 00:10:53,720 So we'll wait until cover that. 154 00:10:53,720 --> 00:10:58,940 In the meantime, let's go back to looking at lambda expressions as function parameters and see how 155 00:10:58,940 --> 00:11:02,900 we can call a function with the lambda expression as the pass parameter. 156 00:11:03,410 --> 00:11:08,450 In the first example, we can see that the lambda expression is defined within the function call to 157 00:11:08,450 --> 00:11:09,050 foo. 158 00:11:09,810 --> 00:11:14,790 This is a common way of passing lambda expressions to functions, since in most cases they're only ever 159 00:11:14,790 --> 00:11:15,810 passed once. 160 00:11:16,940 --> 00:11:21,920 If the lambda will be used more than once, it may be beneficial to assign it to a variable so that 161 00:11:21,920 --> 00:11:26,660 it can be passed to multiple functions and call it independently without having to define the lambda. 162 00:11:26,690 --> 00:11:31,400 Each time, in this case, the lander that can be passed to a function by passing the variable it was 163 00:11:31,400 --> 00:11:32,240 assigned to. 164 00:11:32,270 --> 00:11:33,920 As seen in the second example. 165 00:11:33,920 --> 00:11:39,560 Now let's take a look at why we might want to pass a lambda expression to a function with a simple but 166 00:11:39,560 --> 00:11:42,050 powerful example using a predicate lambda. 167 00:11:43,370 --> 00:11:46,760 Before we get started, let me explain what a predicate Lambda is. 168 00:11:46,880 --> 00:11:52,340 A predicate is a C++ function that takes any number of arguments and returns a Boolean Valley. 169 00:11:53,030 --> 00:11:57,590 So naturally it follows that a predicate lambda is the lambda that implements this functionality. 170 00:11:58,040 --> 00:12:03,470 This may seem like a simple concept, and it is, but this is where the true power of Lambda lies. 171 00:12:04,070 --> 00:12:06,440 In this example, we have a function called print. 172 00:12:06,470 --> 00:12:12,920 If that takes as its parameters, an integer vector and a predicate lambda that's passed as a function 173 00:12:12,920 --> 00:12:13,520 pointer. 174 00:12:13,760 --> 00:12:18,860 In this case, the predicate lambda is used to determine which elements of the integer vector to display 175 00:12:19,040 --> 00:12:20,900 within the main function print. 176 00:12:20,900 --> 00:12:26,900 If is called with the integer vector nums and two different predicate lambdas, the first predicate 177 00:12:26,900 --> 00:12:31,400 lambda returns true if the parameter x is even and false otherwise. 178 00:12:31,610 --> 00:12:36,080 So when it's passed to the print, if the even value of nums will be displayed. 179 00:12:36,920 --> 00:12:41,570 The second predicate Lambda returns true if the parameter x is odd and false otherwise. 180 00:12:41,570 --> 00:12:47,630 So when it's passed into the print, if the odd values of nums will be displayed, you can see that 181 00:12:47,630 --> 00:12:52,400 the same print, if function can be used to display different elements of the vector depending on the 182 00:12:52,400 --> 00:12:54,320 predicate lambda that's passed into it. 183 00:12:55,060 --> 00:13:00,970 This exemplifies the predominant use of lambda expressions and why they're so powerful as we'll see 184 00:13:00,970 --> 00:13:01,660 later. 185 00:13:01,720 --> 00:13:07,000 Predicate lambdas are extremely important when using the standard template library functions and algorithms 186 00:13:07,000 --> 00:13:11,470 such as sort or for each, which require a predicate parameter. 187 00:13:12,040 --> 00:13:17,500 In the next video we'll go over to the ID and we'll run some of these examples in live code. 188 00:13:17,500 --> 00:13:20,110 I'll mark them up so you can see exactly what's happening. 189 00:13:20,230 --> 00:13:21,160 I'll see you there.