WEBVTT

00:00:00.640 --> 00:00:04.930
A course about pointers would not be complete without pointers to

00:00:04.930 --> 00:00:09.630
functions. I will first show you the legacy syntax, which is still used

00:00:09.630 --> 00:00:15.300
in some situations, and then the modern C++ way of using functions as

00:00:15.300 --> 00:00:17.920
function arguments. As you can see,

00:00:17.930 --> 00:00:21.670
I defined a simple function which takes in two integers and

00:00:21.670 --> 00:00:24.450
then returns the result of their addition.

00:00:24.840 --> 00:00:28.410
Every program has a special place in memory where functions

00:00:28.420 --> 00:00:31.560
are stored as a sequence of instructions.

00:00:31.940 --> 00:00:36.930
The function's name is actually a pointer to the place in memory which holds

00:00:36.930 --> 00:00:40.550
the instructions for executing that specific function.

00:00:40.940 --> 00:00:45.220
I can prove this by printing out the function's identifier without

00:00:45.230 --> 00:00:48.240
actually using parentheses to call the function.

00:00:48.560 --> 00:00:50.420
Since this is a pointer to a function,

00:00:50.430 --> 00:00:54.930
the output may vary on different systems, so I will cast it to a void

00:00:54.930 --> 00:00:58.550
pointer just to be sure that we will get a memory address.

00:00:59.440 --> 00:01:01.260
Okay, let's compile this.

00:01:01.940 --> 00:01:05.150
As you can see, the output of this expression is a memory

00:01:05.150 --> 00:01:08.560
address, the address of this function in memory.

00:01:08.940 --> 00:01:13.760
So if functions can have pointers, what do these pointers even look like?

00:01:14.140 --> 00:01:19.750
The syntax for this is inherited from C, and yes, it is a little bit clunky.

00:01:20.140 --> 00:01:24.060
A function pointer needs to be aware of the function signature.

00:01:24.440 --> 00:01:25.650
So, for example,

00:01:25.650 --> 00:01:28.070
if I want to point to a function that has two integers

00:01:28.070 --> 00:01:30.350
parameters and returns an integer,

00:01:30.360 --> 00:01:34.860
I need to explicitly declare the data type of the returned value and

00:01:34.860 --> 00:01:39.430
the data type of every parameter. On the left side of the parameter

00:01:39.430 --> 00:01:43.090
list, which is the usual spot for the function's name, we need to

00:01:43.090 --> 00:01:45.160
define the name of the pointer.

00:01:45.540 --> 00:01:50.870
Of course, since this is a pointer, asterisk is also required. And that's it.

00:01:50.880 --> 00:01:53.360
This is how a pointer to a function looks like.

00:01:53.940 --> 00:01:57.150
Now let's point this pointer to the sum function.

00:01:58.040 --> 00:02:00.950
If you really have a need for a pointer like this one,

00:02:00.960 --> 00:02:03.860
a better way would be to use the auto keyword.

00:02:04.840 --> 00:02:08.759
This will force the compiler to deduce the type of the pointer itself.

00:02:09.539 --> 00:02:12.210
Even though this statement is valid,

00:02:12.220 --> 00:02:16.270
I will add the asterisk next to the pointer name, because I like to

00:02:16.270 --> 00:02:19.420
be explicit and make sure that I don't forget that this is a

00:02:19.420 --> 00:02:22.960
pointer. And now we can use this pointer to access the

00:02:22.960 --> 00:02:25.750
functionality provided by the sum function.

00:02:26.140 --> 00:02:29.840
I can execute this function by dereferencing the pointer and

00:02:29.840 --> 00:02:33.160
then providing the arguments in the usual way.

00:02:33.170 --> 00:02:35.820
Since pointers to functions are really special,

00:02:35.820 --> 00:02:39.370
we can avoid the dereference operator and use the pointer

00:02:39.370 --> 00:02:42.550
like this, just like any regular function.

00:02:42.940 --> 00:02:46.560
Both ways are syntactically correct, but the second one is

00:02:46.560 --> 00:02:48.960
available because it's more convenient.

00:02:49.840 --> 00:02:54.560
I will compile this to prove that it's working, and it does.

00:02:54.940 --> 00:02:57.850
So now that we know how to point to functions,

00:02:57.860 --> 00:03:02.220
let's refactor the code from the calculator program. Instead of having a

00:03:02.220 --> 00:03:06.270
special function for each operation, let's only have one function which

00:03:06.270 --> 00:03:09.060
will do all kinds of different calculations.

00:03:09.540 --> 00:03:14.070
I will name this function calculate. The function definition will

00:03:14.070 --> 00:03:17.260
resemble the definition of two previous functions.

00:03:18.040 --> 00:03:21.790
You might have noticed that the only difference between those previous

00:03:21.790 --> 00:03:26.970
two functions was the arithmetic operator inside of the for loop. If we

00:03:26.970 --> 00:03:31.370
need to add numbers we use the plus operator, and if we need to multiply

00:03:31.370 --> 00:03:36.270
them we can use the multiplication operator, but what if instead of

00:03:36.270 --> 00:03:40.640
directly using these two operators, we can pass these two numbers to a

00:03:40.640 --> 00:03:41.840
special function.

00:03:41.850 --> 00:03:43.760
Let's name this function fun.

00:03:44.640 --> 00:03:49.790
This function will either multiply or add these two numbers, but in any case

00:03:49.800 --> 00:03:54.260
it will return an integer and it will take in two integers.

00:03:55.440 --> 00:03:58.910
We can define this function as a pointer to a function with that kind

00:03:58.910 --> 00:04:03.140
of signature, so this function will actually be a parameter which we

00:04:03.140 --> 00:04:05.460
need to pass to the calculate function.

00:04:06.540 --> 00:04:10.860
Okay, now we actually have to pass the function to this pointer.

00:04:11.240 --> 00:04:16.660
So instead of using the sum function here, we can use the calculate function.

00:04:17.040 --> 00:04:21.560
The third argument needs to be a pointer to a function. Since

00:04:21.560 --> 00:04:24.320
this case is the one where we do addition,

00:04:24.330 --> 00:04:27.840
we could define a function that will do that and then pass

00:04:27.840 --> 00:04:30.350
the name of that function as an argument.

00:04:30.740 --> 00:04:34.320
But this makes no sense, because we will only use that simple

00:04:34.320 --> 00:04:38.750
function here and nowhere else, so why would we name a function

00:04:38.750 --> 00:04:41.450
that will only be used in one place?

00:04:41.940 --> 00:04:47.140
Fortunately, C++ allows us to create anonymous functions, the so‑called

00:04:47.150 --> 00:04:51.720
lambdas. Lambda is a function definition without a name,

00:04:51.730 --> 00:04:55.290
usually for the purposes like this one, where you need a

00:04:55.290 --> 00:04:58.220
function, but you don't want to give it a name because you

00:04:58.220 --> 00:05:00.060
only need it in one place.

00:05:00.840 --> 00:05:03.490
But to turn this definition into a lambda,

00:05:03.500 --> 00:05:06.530
you need to add a pair of these square brackets before

00:05:06.530 --> 00:05:10.000
the argument list. And that's it.

00:05:10.010 --> 00:05:14.350
We defined an inline function which will add these two numbers, and we

00:05:14.350 --> 00:05:17.160
passed it to the calculate function as a pointer.

00:05:17.540 --> 00:05:22.250
Let's do the same for multiplication, but don't forget to change this operator.

00:05:23.140 --> 00:05:25.260
Okay, let's compile the program.

00:05:26.340 --> 00:05:30.450
I will pick some random numbers and choose multiplication.

00:05:30.840 --> 00:05:33.560
I think that this result is correct.

00:05:34.040 --> 00:05:34.660
Great.

00:05:35.840 --> 00:05:40.400
Lambdas can be assigned to legacy function pointers only if they don't

00:05:40.400 --> 00:05:45.460
contain any captures. Captures are defined inside of these square

00:05:45.460 --> 00:05:49.430
brackets as a list of variables from the surrounding scope that you want

00:05:49.430 --> 00:05:51.970
to make available inside of the lambda,

00:05:51.980 --> 00:05:56.560
basically making your function aware of the outside context.

00:05:57.040 --> 00:06:00.730
We will not cover this, because this course is not about lambdas,

00:06:00.730 --> 00:06:04.810
but if you use these captures, then you cannot point to this

00:06:04.810 --> 00:06:07.450
function with a simple function pointer.

00:06:07.840 --> 00:06:08.540
Instead,

00:06:08.550 --> 00:06:11.540
you should use a special standard function class

00:06:11.550 --> 00:06:14.650
available from the C++ functional library.

00:06:15.040 --> 00:06:17.460
So let's first include that library.

00:06:18.340 --> 00:06:22.860
I will remove this pointer and define the std::function parameter.

00:06:23.940 --> 00:06:27.760
This is a templated class, so you need to pass in the function

00:06:27.760 --> 00:06:30.750
signature of the function that you want to receive.

00:06:31.140 --> 00:06:35.070
I will also turn this parameter into a constant reference, because I want

00:06:35.070 --> 00:06:39.420
to pass it by reference, and I made it constant because anonymous

00:06:39.420 --> 00:06:42.750
functions cannot be modified after their definition.

00:06:43.940 --> 00:06:44.710
Okay,

00:06:44.720 --> 00:06:49.870
let's compile this to check if it works. And it seems

00:06:49.870 --> 00:06:51.860
that it does, just like before.

00:06:52.240 --> 00:06:55.490
Since this is C++, you should always prefer using

00:06:55.490 --> 00:06:58.750
std::function class instead of a raw pointer.

00:06:59.340 --> 00:07:03.590
It does have a slight overhead, but it's unnoticeable in most cases,

00:07:03.590 --> 00:07:06.680
so unless you have a good reason to use a raw pointer,

00:07:06.690 --> 00:07:11.850
always use the std::function class, especially if you are working with lambdas.
