In this section, we will implement a program that reads integers from the user and sums them up. Because the user can always input random things instead of numbers, we will see how optional can improve our error handling:
- First, we include all the needed headers and declare that we use the std namespace:
#include <iostream>
#include <optional>
using namespace std;
- Let's define an integer type, which, maybe, contains a value. The std::optional type does exactly that. By wrapping any type into optional, we give it an additional possible state, which reflects that it currently has no value:
using oint = optional<int>;
- By having defined an optional integer type, we can express that a function that usually returns an integer can also possibly fail. If we take an integer from user input, this can possibly fail because the user might not always enter an integer even though we asked him to do so. Returning an optional integer is perfect in this case. If reading an integer succeeds, we feed it into the optional<int> constructor. Otherwise, we return a default constructed optional, which signals failure or emptiness:
oint read_int()
{
int i;
if (cin >> i) { return {i}; }
return {};
}
- We can do more than returning integers from functions that can possibly fail. What if we calculate the sum of two optional integers? This can only lead to a real numeric sum if both the operands contain an actual value. In any other case, we return an empty optional variable. This function needs a little more explanation: by implicitly transforming the optional<int> variables, a and b, to boolean expressions (by writing !a and !b), we get to know whether they contain actual values. If they do, we can access them like pointers or iterators by simply dereferencing them with *a and *b:
oint operator+(oint a, oint b)
{
if (!a || !b) { return {}; }
return {*a + *b};
}
- Adding a normal integer to an optional integer follows the same logic:
oint operator+(oint a, int b)
{
if (!a) { return {}; }
return {*a + b};
}
- Let's now write a program that does something with optional integers. We let the user enter two numbers:
int main()
{
cout << "Please enter 2 integers.n> ";
auto a {read_int()};
auto b {read_int()};
- Then we add those input numbers and additionally add the value 10 to their sum. Since a and b are optional integers, sum will also be an optional integer type variable:
auto sum (a + b + 10);
- If a and/or b do not contain a value, then sum cannot possibly contain a value either. The nice thing about our optional integers now is that we do not need to explicitly check a and b. What happens when we sum up empty optionals is perfectly sane and defined behavior because we defined operator+ in a safe way for those types. This way, we can arbitrarily add many possibly empty optional integers, and we'll only need to check the resulting optional value. If it contains a value, then we can safely access and print it:
if (sum) {
cout << *a << " + " << *b << " + 10 = "
<< *sum << 'n';
- If the user enters something non-numeric, we error out:
} else {
cout << "sorry, the input was "
"something else than 2 numbers.n";
}
}
- That's it. When we compile and run the program, we get the following output:
$ ./optional
Please enter 2 integers.
> 1 2
1 + 2 + 10 = 13
- Running the program again and entering something non-numeric yields the error message we prepared for this case:
$ ./optional
Please enter 2 integers.
> 2 z
sorry, the input was something else than 2 numbers.