WEBVTT

00:02.580 --> 00:08.160
Hello again! In this video, we're going to look at if statements and switch in C++ 17.

00:10.540 --> 00:17.200
Let's start off with something that we already know about. In C and early versions of C++ if you want

00:17.200 --> 00:18.070
to write a for loop,

00:18.370 --> 00:21.400
you have to declare the loop counter before the loop.

00:22.120 --> 00:29.530
So this means that this variable i, which we are going to use to iterate over - some of array, presumably - is

00:29.530 --> 00:31.510
actually defined in the enclosing scope.

00:32.950 --> 00:36.970
So after the loop has finished, this variable i will still be in existence.

00:37.480 --> 00:39.880
We can't create another variable called i.

00:40.780 --> 00:43.780
And if you want another loop, you either have to use a different variable

00:44.320 --> 00:49.720
Or we have to re-use this variable i, which in itself can create bugs.

00:49.990 --> 00:54.700
If someone else has decided to use that variable and then we change the value to something that they were not

00:54.700 --> 00:55.270
expecting.

00:56.780 --> 01:04.620
In C++ 98, you now allowed to declare the variable inside the for loop, so this is a for loop initializer.

01:04.640 --> 01:12.830
i is now a local variable inside the loop and once the loop is complete, then the variable i no longer

01:12.830 --> 01:13.370
exists.

01:14.780 --> 01:18.500
And if you want to write another loop, you can just do the same thing, "for (int i ..."

01:19.400 --> 01:21.220
And you will get your own version of i.

01:24.160 --> 01:30.670
So this is really a better way of doing things, because the loop counter is really an internal detail

01:30.670 --> 01:36.340
of the loop. Code outside the deep should not need to know what type the loop counter is, what its name

01:36.340 --> 01:37.330
is, what its value is.

01:38.230 --> 01:43.720
If you do need to know the final value of the loop counter, you can always define a variable outside the

01:43.720 --> 01:45.070
loop and copy into it.

01:47.830 --> 01:53.170
So this makes your code clearer, more concise and safer. Which is all good stuff!

01:59.750 --> 02:05.300
We often have situations where we want to check the return value from a function before we do something.

02:05.870 --> 02:12.170
So this normally means we have to define a variable to hold the returned value and then write an statement.

02:12.920 --> 02:14.660
But really, this is just a single operation.

02:14.660 --> 02:17.480
If the return value is so and so, do this.

02:20.360 --> 02:26.030
In this example, we are calling begin() to get the first element of a vector, and we are using this variable

02:26.030 --> 02:31.220
"iter" to store the result, and then we want to make sure that the vector is not empty.

02:31.580 --> 02:35.900
If the vector is empty, then the results of calling begin() is the same as the results of calling end().

02:36.950 --> 02:42.860
So we want to check the vector is not empty before we process it. In C++17,

02:42.860 --> 02:44.570
you can do this all in a single statement.

02:44.570 --> 02:47.120
You can initialize the iterator.

02:47.630 --> 02:54.200
So we have this initialization statement here and then semicolon and then we have our condition.

02:55.520 --> 03:01.310
So this defines iter as a local variable to the if statement, and then it does the check.

03:01.610 --> 03:05.180
And if this test is true, then it will execute the body of the if statement.

03:05.540 --> 03:10.670
And this has similar advantages to the loop initializer. It does not get mixed up with the rest of

03:10.670 --> 03:11.060
the code.

03:12.440 --> 03:18.860
When we say that iter is local to the if statement, that also includes an "else" block, which corresponds to

03:18.860 --> 03:19.290
the if.

03:20.180 --> 03:25.760
So in this case, it's not really very useful because the else statement will only happen if the iterator

03:25.760 --> 03:28.520
is invalid, which means you cannot do anything useful with it.

03:29.210 --> 03:32.600
But in other situations, with a normal variable, that might be useful.

03:34.600 --> 03:39.910
So here is that code in Visual Studio. We have our vector.

03:40.960 --> 03:47.500
Then the old way. We get the iterator to the first element, then we compare it to the result of

03:47.500 --> 03:48.100
calling end().

03:49.000 --> 03:52.890
And if that is different, then it is safe to use the iterator.

03:54.490 --> 04:00.700
Then we have the C++17 version, where we initialize the iterator inside the if statement.

04:01.480 --> 04:02.800
So this will call begin(),

04:03.220 --> 04:08.320
store the result and then compare it to the result of calling end. Unfortunately,

04:10.290 --> 04:17.760
Visual C++ doesn't actually support this. "Requires C++ 17", but if you look in the project properties,

04:20.090 --> 04:21.990
we are using C++ 17!

04:22.010 --> 04:27.590
So there you are, four years after C++ 17, and it cannot - and it is still not fully supported.

04:28.070 --> 04:31.430
People often ask me why I do not cover C++ 20 in my courses.

04:31.940 --> 04:38.900
There is the reason. We still do not have complete support for C++ 17. And C++ 20 is obviously much less

04:38.900 --> 04:39.410
complete.

04:41.450 --> 04:50.600
So I am going to use MinGW instead, I use Notepad++ and MinGW for writing code for videos, on Windows.

04:51.080 --> 04:54.350
I only use Visual Studio for demonstrations, because it is a bit easier.

04:55.340 --> 04:56.750
So this is MinGW.

04:56.750 --> 05:00.230
This is a port of GNU C++ for Windows.

05:00.860 --> 05:07.320
The editor is Notepad++, which is a free program. Not the Notepad that comes with windows!

05:07.340 --> 05:10.370
This is quite different. And infinitely better!

05:12.460 --> 05:13.930
So I have got the same code here.

05:14.260 --> 05:15.690
I am not sure if you can actually see that,

05:15.820 --> 05:18.160
because it is a bit smaller, but it is actually the same. Honest!

05:26.100 --> 05:30.420
So that's how you get C++ 17 with GCC and also clang.

05:31.380 --> 05:32.520
And then the name of the file.

05:35.350 --> 05:41.440
And right, so it does actually know how to compile C++ 17 code. And then we can execute it.

05:41.530 --> 05:46.300
The output file is called "a.exe", unless you ask for it to be called something else.

05:49.970 --> 05:53.570
So there we are, we get the same resultswith both forms of the code.

05:59.020 --> 06:04.900
And you can also do this with a switch statement. So instead of creating a variable to store the result

06:04.900 --> 06:09.460
of the function call and then switching on that, you can do it all in a single statement.

06:12.130 --> 06:15.310
So here's a simple program which uses a switch statements.

06:16.180 --> 06:20.260
I mean, this is really C code, iterating over array with an integer.

06:20.380 --> 06:27.400
So perhaps it's not the best example of Modern C++! But we do have this C++ 17 feature with the initializer

06:27.400 --> 06:28.180
in the switch statement.

06:29.770 --> 06:35.470
So we have this C-style string with some whitespace characters. We have some spaces and also

06:35.470 --> 06:37.420
this tab character.

06:38.620 --> 06:44.080
And the point of this program is to count the number of spaces in this string.

06:44.890 --> 06:49.270
We have this variable here that we're going to use to count the number of whitespaces.

06:49.510 --> 06:53.140
So every time we encounter a whitespace character, we're going to increment it.

06:55.890 --> 06:58.080
And then we iterate over this array.

06:58.110 --> 07:03.630
And then for each element, we gets the current character and we have a switch.

07:05.190 --> 07:09.870
So let's remind ourselves how switch statements work. When the program runs, the

07:11.100 --> 07:19.050
program will examine this character 'c', so this is some numeric value, and then it will jump to the case label that matches it.

07:19.470 --> 07:25.860
So if C is a space character, it will jump to this case, if it is a tab, it will jump to here and if it is a newline, it will

07:25.860 --> 07:26.460
jump to here.

07:27.570 --> 07:32.580
And then once it has jumped to a case, it will execute all the code until it encounters a break statement.

07:33.330 --> 07:36.720
So it can actually execute across case labels.

07:38.490 --> 07:44.100
So this means if c is a space or a tab or a new line, it is going to execute the same code, which is to

07:44.100 --> 07:45.060
increment the counter.

07:45.600 --> 07:50.400
And then when it gets to the break, it will jump out of the switch statement and go on to the next iteration

07:50.400 --> 07:51.000
through the loop.

07:52.080 --> 07:59.430
If it is anything other than a space, tab or newline, it is going to jump to the default label. Which will

07:59.430 --> 08:02.190
just jump out of the switch again and go to the next element.

08:05.340 --> 08:09.300
So again, Visual C++ will not compile this code, so we have to get back to...

08:19.870 --> 08:22.930
So there we are. The text is, "How much white space<tab> in here?"

08:23.560 --> 08:27.130
And the answer is five, so one, two, three.

08:27.810 --> 08:28.350
Four.

08:29.990 --> 08:30.380
Five.

08:30.680 --> 08:33.680
Yes, there is an extra space after the tab. That confused me!

08:38.240 --> 08:43.160
So we say in this case that the code will fall through the case labels until it finds a break statement.

08:44.690 --> 08:48.050
There were other languages which work differently. In some languages,

08:48.050 --> 08:50.150
only one case statement is ever executed.

08:50.480 --> 08:55.870
So if you have an empty case that would presumably be an error. But in C++, it just keeps falling through

08:55.880 --> 08:57.830
until it finds a break statement.

08:59.300 --> 09:01.460
And this can be quite useful as in this program.

09:02.240 --> 09:07.700
On the other hand, if you make a mistake and forget to put in a break statement, then it can be rather

09:08.090 --> 09:08.420
bad.

09:10.450 --> 09:17.920
So in this example, we have people who work in different locations in the company, and we want to

09:17.920 --> 09:19.930
assign them a car, depending on where they work.

09:20.500 --> 09:26.830
So we have this employee location, which is presumably some sort of enumerated type, and we do a switch

09:26.830 --> 09:27.130
on it.

09:27.790 --> 09:34.780
So if they work in head office, they get a nice swanky car. If they work in sales they get a fairly average

09:34.780 --> 09:35.110
car.

09:35.980 --> 09:38.650
And if they work in the warehouse, they get to drive a truck.

09:40.300 --> 09:41.710
So that's the theory anyway.

09:41.710 --> 09:44.320
But the programmer forgot to put a break statement in.

09:44.650 --> 09:50.200
So when this runs, if the employee location is head office, then it's going to set the vehicle type

09:50.200 --> 09:56.020
to Mercedes. Then it's going to fall through to sales. Then it wil set the vehicle type to Peugeot. Then it will fall

09:56.020 --> 09:58.030
through and set the vehicle type to truck.

09:58.690 --> 10:04.060
So in fact, every employee will end up driving a truck, which is probably not going to make you very

10:04.060 --> 10:04.480
popular!

10:08.170 --> 10:12.850
Some compilers will actually give you a warning here, which is quite helpful because it tells you that

10:12.850 --> 10:18.400
you may need to check program logic. On the other hand, if you did actually intend to fall through,

10:18.730 --> 10:19.420
then it is annoying.

10:19.720 --> 10:21.730
It is just more noise in your compiler output.

10:22.420 --> 10:27.130
And if you have a setting which treats all warnings as errors. Or if you have a coding standard

10:27.130 --> 10:31.840
which says you are not allowed to have any warnings at all, then it is very annoying!

10:32.680 --> 10:36.580
What usually happens in that case is people just get annoyed and hack their code to shut the compiler up,

10:36.880 --> 10:38.530
which is not really what we want to happen.

10:42.300 --> 10:44.730
C++ has things called attributes.

10:44.820 --> 10:47.100
These are nothing like Java attributes!

10:47.460 --> 10:52.920
You can't go and implement a database query system with them. An attribute is just simply an instruction

10:52.920 --> 10:55.770
to the compiler, and it cannot change the meaning of the code.

10:59.150 --> 11:04.760
To write an attribute, we put it inside a pair of square brackets, and the language defines quite

11:04.760 --> 11:05.360
a few of these.

11:05.660 --> 11:06.740
I'll be going through all of them.

11:07.100 --> 11:11.960
But as we are talking about switches and C++ 17, this seems like a good point to cover it.

11:13.880 --> 11:19.940
So to use the fallthrough attribute, we put it in an empty statement. So that just means we put a semicolon

11:19.940 --> 11:25.390
after it, like that. And that will tell the compiler that we mean to fall through and it is not a mistake

11:25.400 --> 11:26.930
and please do not give a warning.

11:31.870 --> 11:36.820
So here is the code in visual C++ again. Which again, does not compile.

11:48.860 --> 11:49.460
So we are.

11:50.750 --> 11:52.640
The code compiles without any warnings.

11:52.880 --> 11:55.910
So if we - Let's say we try to make it warn on everything.

12:02.280 --> 12:07.410
OK, we get a warning for using an unused variable, but we do not get the [warning] for falling

12:07.410 --> 12:09.450
through, which is what we are trying to avoid.

12:12.550 --> 12:14.170
OK, so that's it for this video.

12:14.680 --> 12:17.560
I'll see you next time, but meanwhile, keep coding!
