WEBVTT

00:00.120 --> 00:04.260
Hello again! In this video, we are going to look at the decltype keyword.

00:05.550 --> 00:09.130
You may remember the unique pointer with a custom deleter.

00:09.780 --> 00:14.760
We needed to have a template parameter, which was the same type as a lambda expression.

00:15.600 --> 00:19.860
And then I threw in this decltype, which made everything magically work.

00:20.370 --> 00:24.360
So in this video we are going to find out what this decltype keyword actually does.

00:26.580 --> 00:30.060
The decltype keyword was added in C++11.

00:30.630 --> 00:33.030
It can only be used at compile time.

00:34.110 --> 00:41.220
decltype gives the type of its argument, and this argument must be an expression or an object. Before

00:41.220 --> 00:42.300
C++11,

00:42.540 --> 00:47.610
Some compilers offered this feature using a non-standard extension, "typeof".

00:48.360 --> 00:54.270
There were various implementations, and the one for C++11 was going to be different. So they decided

00:54.330 --> 00:55.890
to give it a different name as well.

00:56.160 --> 00:57.600
So we have to decltype.

01:00.370 --> 01:07.180
decltype does not evaluate its argument, and it does not cause any code to be executed. Either at

01:07.180 --> 01:08.590
run-time or compile time.

01:09.790 --> 01:15.850
All that happens, is that the compiler will replace the decltype with whatever type the argument

01:15.850 --> 01:16.600
would return.

01:17.020 --> 01:23.020
For example, if we have decltype 1 + 2, then the compiler will see that 1 + 2

01:23.050 --> 01:28.660
will return something which is an int, and it will replace "decltype(1 + 2)" by int.

01:29.650 --> 01:31.060
And then we have int y.

01:31.630 --> 01:36.970
So this will declare y as a variable, which has the same type as 1 + 2.

01:39.150 --> 01:45.990
If the argument is a named variable, then decltype will be replaced by the type that this argument

01:46.350 --> 01:47.160
was declared as.

01:47.160 --> 01:55.740
So we have here a variable with the name "x", declared as type int, so "decltype(x)" will be replaced by

01:55.740 --> 01:59.160
int, which was the type that x was declared as.

02:00.790 --> 02:04.810
You may think this is rather similar to "auto", and in some ways it is.

02:05.740 --> 02:11.890
We mostly use "auto" when we are declaring a variable, and we want this variable to have the same type as the

02:11.890 --> 02:12.490
initializer.

02:13.390 --> 02:19.720
So here we have a variable "x" which will have the same type as 6, and this will have the initial value

02:19.720 --> 02:20.050
6.

02:20.560 --> 02:23.830
So we are declaring "x" as an int, with initial value 6.

02:24.640 --> 02:30.940
decltype is used with an existing object, or with an expression, and it is replaced by the type of that

02:30.940 --> 02:31.480
expression.

02:32.440 --> 02:34.330
So here we have "decltype(x)".

02:34.660 --> 02:37.210
So this will be replaced by the type of "x", which is int.

02:38.290 --> 02:40.630
So we have a variable "y", which is type int.

02:42.580 --> 02:46.420
There is another difference, which is that decltype will retain the qualifiers.

02:46.750 --> 02:52.000
So if we have a const or reference, then "auto" will drop those.

02:52.360 --> 03:00.370
So "auto y". In this case, "y" will just be an int, not a const int. "decltype(cx)" will be replaced by

03:00.370 --> 03:03.400
the type of "cx", which is const int.

03:03.910 --> 03:06.160
So, "cz" will be a const.

03:08.960 --> 03:10.160
So let's try this out.

03:10.160 --> 03:12.620
We have "x", which is a const int.

03:13.340 --> 03:16.220
Then we have "y", which is declared as "auto".

03:16.610 --> 03:18.230
So "y" will have the same type as int,

03:18.680 --> 03:23.840
but without the const. Then we have "z", which is declared as "decltype(x)".

03:24.590 --> 03:27.040
So this will have the same type that "x" was declared as.

03:27.650 --> 03:28.970
So this will include the const.

03:30.230 --> 03:31.670
So we can increment "y".

03:34.930 --> 03:36.580
And that has incremented "y".

03:37.450 --> 03:44.170
But if we try to increment "z", which was declared as "decltype(x)", then we cannot increment that.

03:44.170 --> 03:45.760
Because "z" is a constant.

03:47.030 --> 03:48.260
The rules for decltype.

03:48.260 --> 03:55.370
They may seem a bit complicated, but in effect, decltype will do what you expect. If you have

03:55.370 --> 03:56.600
decltype with a named variable.

03:56.630 --> 03:59.060
We have already mentioned that. You get the declared type.

04:00.050 --> 04:05.510
If we have decltype with an argument which is an lvalue expression, then the result will be an

04:05.510 --> 04:09.140
lvalue reference to the deduced type of the expression.

04:09.740 --> 04:17.060
So, if "x" and "y" are both ints here, then "decltype(x + y)" will give reference to int.

04:18.540 --> 04:22.020
If we put a variable inside a pair of brackets, then it becomes an expression.

04:22.530 --> 04:27.180
So decltype of "x" inside brackets, will be a reference to int.

04:29.720 --> 04:32.090
For rvalues. If we have a pure rvalue,

04:32.100 --> 04:35.890
So that is a literal, then this will be replaced by the type of the literal.

04:36.350 --> 04:41.780
So "decltype(2)" will be an int. If we have an xvalue, an "expiring" value.

04:41.780 --> 04:47.930
So that is a temporary value. Then the compiler will deduce the type of the argument, and replace it

04:47.930 --> 04:50.060
by an rvalue reference to that type.

04:50.630 --> 04:56.030
So if we have a temporary object of class Test, then this will be replaced by an rvalue reference

04:56.030 --> 04:56.690
to a Test.

04:59.710 --> 05:04.600
So, decltype of a variable will be the type of the variable.

05:04.600 --> 05:05.590
So "y" will be int.

05:08.330 --> 05:12.560
decltype of an lvalue expression will be an lvalue reference.

05:12.560 --> 05:20.570
So "p" will be an lvalue reference to int. Then with a pure rvalue, "z" will be an int. And with

05:20.570 --> 05:24.440
a temporary object, "t" will be rvalue reference to a Test.

05:26.900 --> 05:33.260
In C++14, we can use "auto" as the argument to decltype, which seems rather a weird thing to do.

05:34.730 --> 05:39.980
The idea of this is to have something which works exactly like "auto", but without dropping the qualifiers.

05:41.120 --> 05:47.090
So if we have "auto b", initialized from "a", then "b" is just going to be an int.

05:47.660 --> 05:54.920
If we use "decltype(auto)" then this will keep the const and the ref, and "c" will be a constant reference

05:54.920 --> 05:55.630
to int.

05:57.980 --> 06:04.430
So we have the same thing again, but this time using "decltype(auto)". So we can increment "b", because

06:04.430 --> 06:05.900
"b" is not const.

06:08.150 --> 06:12.320
But "c" is a const ref, so we cannot increment it.

06:14.370 --> 06:16.200
And finally, when would you use this?

06:16.710 --> 06:19.350
Well, decltype is mainly useful in compile-time

06:19.440 --> 06:19.920
programming.

06:20.880 --> 06:24.510
The classic example is for the return type of a template function.

06:25.260 --> 06:30.960
In C++11, you can have a function which returns auto. But you have to give the return type, which

06:30.960 --> 06:33.570
rather seems to defeat the point of returning auto.

06:34.680 --> 06:37.110
But you can use decltype here.

06:37.980 --> 06:43.170
So, if you have a template function which returns the sum of its arguments, what will the return type

06:43.170 --> 06:43.470
be?

06:44.460 --> 06:49.230
If the arguments have the same type, then it is obvious. The return type will be the type of the arguments.

06:49.860 --> 06:54.780
If the arguments have different types, then it may not be. If an argument is an int and another

06:54.780 --> 06:57.330
argument is double, then the return type needs to be double.

06:58.260 --> 07:03.570
If you have a character pointer and an int, then the return value needs to be a character pointer.

07:05.820 --> 07:07.840
So with decltype, this is not a problem.

07:07.870 --> 07:13.050
You can ask the compiler, what will be the return value from adding these? And then the compiler will

07:13.050 --> 07:15.660
replace this by the returned type.

07:17.040 --> 07:23.610
So when this is instantiated, the return type of this function template will be the return type for

07:23.720 --> 07:27.450
adding the int and double, or the character pointer

07:27.450 --> 07:28.620
and int.

07:30.210 --> 07:34.230
You can also use decltype with the generic lambdas in C++14.

07:34.230 --> 07:37.770
So these are lambdas which have arguments of type "auto".

07:38.610 --> 07:46.520
And then, in the lambda body, you could have variables which are of this type. As a rather silly example,

07:46.530 --> 07:52.200
you can return a vector of elements which have the same type as the parameter. I mean, you could just call

07:52.200 --> 07:58.200
the vector constructor directly. But it is an example! And then we use the "auto" return value as well.

07:58.200 --> 08:00.480
So the company will deduce the return type.

08:03.150 --> 08:05.070
So here is our template function.

08:05.610 --> 08:08.010
The return type will be the type

08:08.010 --> 08:15.240
of adding the arguments to the function. Then we have our generic lambda which will return a vector,

08:15.270 --> 08:17.550
whose elements are the same type as its arguments.

08:19.470 --> 08:25.500
And then we call our template function, with arguments 2 and 2. And a C string and and an offset.

08:26.940 --> 08:29.280
And then we call our "make_vector" with the same arguments.

08:31.770 --> 08:34.980
So, adding 2 and 2 gives 5. Adding the C string, "hello"

08:34.980 --> 08:38.700
and 2 gives the C string, with the first two characters missing.

08:39.300 --> 08:44.850
"make_vector" with arguments 2 and 3 gives a vector with 3 elements of value 2, type int.

08:45.540 --> 08:52.170
And doing that with a C string, gives a vector with two elements, which are C strings.

08:53.190 --> 08:58.560
Okay, that is it for this video, and this section, and the language part of the course.

08:58.980 --> 09:00.630
I will see you next time in the project.

09:00.630 --> 09:02.670
But until then, keep coding!
