using namespace std;
2. Определим целочисленный тип, который, может быть, содержит значение. Нам идеально подойдет тип std::optional. Обернув любой тип в тип optional, задаем ему еще одно возможное состояние, которое отражает тот факт, что тип
using oint = optional
3. Определив необязательный целочисленный тип, можем выразить тот факт, что функция, которая возвращает целое число, тоже способна дать сбой. Если мы возьмем целое число из пользовательских входных данных, то существует вероятность сбоя, поскольку пользователь может ввести какой-то другой символ, несмотря на наше приглашение. Возврат целого числа, обернутого в тип optional
optional
. В противном случае вернем экземпляр типа optional, созданный с помощью конструктора по умолчанию, что говорит о сбое или пустоте.oint read_int()
{
int i;
if (cin >> i) { return {i}; }
return {};
}
4. Наши возможности не ограничиваются возвратом целых чисел из функций, способных дать сбой. Что если мы подсчитаем сумму двух целых чисел, которые могут не содержать значений? Мы получим реальную численную сумму только в том случае, если оба операнда содержат значения. В любом другом нужно вернуть пустую переменную типа optional
a
b
, к булевым выражениям (с помощью конструкций !a
и !b
), мы узнаем, содержат ли они реальные значения. Если да, то можно получить к ним доступ как к указателям или итераторам, просто разыменовав их с использованием конструкций *a
и *b
:oint operator+(oint a, oint b)
{
if (!a || !b) { return {}; }
return {*a + *b};
}
5. Сложение обычного целого числа и целого числа, которое может не содержать значений, следует той же логике:
oint operator+(oint a, int b)
{
if (!a) { return {}; }
return {*a + b};
}
6. Теперь напишем программу, совершающую некие действия с целыми числами, которые могут не содержать значений. Пригласим пользователя ввести два числа:
int main()
{
cout << "Please enter 2 integers.\n> ";
auto a {read_int()};
auto b {read_int()};
7. Сложим эти числа, а затем добавим к полученной сумме значение 10
a
и b
могут не иметь значений, sum
также будет необязательной целочисленной переменной: auto sum (a + b + 10);
8. Если переменные a
b
не содержат значений, то sum не может иметь значения. Положительный момент заключается в том, что не нужно явно проверять значения переменных a
и b
. Сложение пустых экземпляров типа optional
— полностью корректное поведение, поскольку мы определили безопасный оператор +
для этих типов. Таким образом можно произвольно сложить несколько пустых необязательных экземпляров, а проверять нужно только полученный экземпляр. Если он содержит значение, то мы можем безопасно получить к нему доступ и вывести его на экран: if (sum) {
cout << *a << " + " << *b << " + 10 = "
<< *sum << '\n';
9. Если пользователь вводит не числа, то мы сообщаем об ошибке:
} else {
cout << "sorry, the input was "
"something else than 2 numbers.\n";
}
}
10. На этом все. Компиляция и запуск программы дадут следующий результат:
$ ./optional
Please enter 2 integers.
> 1 2
1 + 2 + 10 = 13
11. Если мы запустим программу снова и введем не числа, то увидим сообщение об ошибке, подготовленное нами для таких случаев:
$ ./optional
Please enter 2 integers.
> 2 z
sorry, the input was something else than 2 numbers.
Как это работает
Работать с типом optional
T
имел дополнительное состояние, указывающее на возможный сбой, то можем обернуть его в тип std::optional
.Когда мы получаем экземпляр подобного типа откуда бы ни было, нужно проверить, он пуст или же содержит значение. Здесь поможет функция optional::has_value()
true
, то можно получить доступ к этому значению. Это позволяет сделать вызов T& optional::value()
.