- [Instructor] Optionals in C++.
Okay. What's our use case?
Soon we're gonna implement hash tables in C++
and as you recall, our hash table may or may not
have a value stored at any given index.
Some elements may be empty.
Then the question becomes what to do
with empty elements in our vector?
One approach is to initialize the elements of our vector
with some sentinel value, like negative one or something
to indicate that we haven't stored anything yet.
And this is cumbersome and it would complicate
our implementation of hash tables.
So instead, we're going to use C++ optionals.
Now, what is a C++ optional?
A C++ optional is just a wrapper around a value
that can indicate whether or not
there is a value, or if it's just null.
We specify a type as being optional,
for example, we would use optional int
and that would represent something
that could be empty, or an int
and using C++ optionals for the elements of our hash table
will give us a convenient way of handling empty elements.
Now I'll show you how to use C++ optionals.
This might seem a little complicated at first,
so we're gonna create a new file, optionals.
Okay, now so far for this class,
we've been using C++ standard 14.
Optionals were not fully included in C++ standard until 17.
They were made available on an experimental basis in 14
but your particular installation,
your particular compiler may not have the libraries,
the experimental libraries.
So what we're gonna do first is we're going to test
to see if you're using C++ 14 or 17
and then include the appropriate files
based on our standard of C++.
So the first thing we're going to do
is we're going to test the standard of C++ that we're using
and then we're going to include
the appropriate libraries, depending on that version.
And so we have a directive
that you haven't seen before in this class
and that's if underscore underscore C++ less than 17.
So this is testing the version that we're using,
if the version of C++.
This is a variable that the compiler can see
and we're just checking.
Is the version less than 17?
Is the standard less than 17, I should say?
Okay and if that's the case,
we're going to use the libraries that are experimental.
So include experimental, sorry, optional.
Then we're going to use components from that library
using standard experimental, optional.
And you'll recall this is the scope resolution operator,
so we're actually looking in the standard library
in the experimental sub library, the optional element.
Okay and then using standard experimental, nullopt
and that's the value that's returned
when we have an empty element
and then using, just for the sake of completeness,
std experimental make_optional, okay?
Now we have an else and what we put here
will be processed by the compiler
if our standard is 17 or greater.
And so we're going to include optional.
Okay and then using standard optional
using std nullopt opt,
using, sorry, std make_optional.
All right and then we end our if block.
End if.
Okay, so this is a conditional
and when our compiler runs, it's gonna check to see
if our standard that we've specified is less than 17.
If it is, it's going to try to include
our experimental optionals
and then we'll use these components.
Otherwise, it's going to try to use
the optional that's part of the C++ 17 standard, okay?
If this gives you trouble,
if this part gives you trouble
and you can see I'm having a little trouble here
then what you do is you modify, I'm sorry.
You modify your CMAKE list to specify version 17.
Reload those changes and that should make everything happy.
Okay, so that works for me.
So your mileage will vary, depending on your installation
of your compiler or the libraries that you have available.
But the idea is to make these optional nullopt
and make_optional available to you.
And that's also by the way, a reasonable use case
for allowing using standard within a header file
because we want optional null up and make optional
but we don't wanna have to include the full scope operator
within our code, because that may vary, okay?
Here it's standard, here it's standard experimental.
So that's why we're allowing using
or we would allow using a header file
if this were in fact a header file.
We're also gonna include vector.
Include iostream and include string.
Okay, now what we're gonna do is we're going to create
a function that makes use of optional
and like I said in the slides earlier,
we can specify something like this, optional int,
which means it could be an integer, or it could be null.
This function could return an integer
or it could return an empty value
and we'll call that find.
And what we're gonna do is we're going
to be hunting through a vector.
We're just gonna use a simple linear search
for this demo.
So vector of std string.
V and some key that we wanna look for.
std string key.
Okay. So this function is gonna take a vector of strings.
It's gonna look for some key in that vector
and it's going to return the index,
if it finds it, or a nullopt, if it's not found.
Okay, so that's convenient.
For int i = 0
i less than the v.size ++i.
What are we gonna do?
We're just gonna go through our vector.
If the sub-i = key, we're gonna return i.
Otherwise, if we get through the whole vector
and we haven't found our key, we return nullopt, okay?
And now we need a little driver code
to make all this fun stuff happen.
So let's have int main and let's have a standard vector.
Call it strings.
Call it V, as we've been doing all along
and we'll give it the names of the days of the week.
Monday, Tuesday, Wednesday,
Thursday, Friday,
Saturday and Sunday.
One of these days, I'm gonna learn
how to type while I'm talking, sorry.
Okay, so that's our input vector
and then we're going to call find
and what does find return?
It returns an optional int.
Could be an int, it could be a null, i = find v.
Find in v the string Thursday.
Okay? And then we're gonna do a test.
If i, then we're going to print the index
and with the optional wrapper,
what we get is a null value or a pointer to the value.
So cout, we need to de-reference that pointer.
And so when this code runs, when this code executes,
we have indices zero one, two, three, okay?
So when we run find on this input vector,
it's going to return the value three
because that's the index where the value at i
matches the string that we passed in.
And then we get this wrapper back
and we check to see if it's empty
and if it's not, then we know we have a value
and so we wanna de-reference that value
and send it to a standard output.
So that's gonna print three
and now let's do a negative case
where i = find v
and we'll put in a string that's not there, wombat.
Wombat. Okay.
And then if i, then the same thing.
And then return zero.
But let's think about what's gonna happen here.
What's gonna happen is wombat is not in our vector.
And so when we call find on our vector
and the string wombat,
it's gonna iterate through our vector.
It's going to find that it's not there
and it's gonna return nullopt.
So when we test, if i, this is gonna be false,
this is gonna be a falsie thing.
And so this won't print print,
because wombat is not in our vector
and thus find will return nullopt.
So if we run this code
it's gonna print three and nothing else, okay?
And let's run it.
And there we go, we got that integer back
and then this part, as expected, did not print.
And that's how optionals work.
And again, we're gonna use those in our hash tables
to indicate whether a particular index
in our hash table has a value or not.
That's all for now.