Вместо того чтобы всегда использовать конструкции if (x.has_value()) {...}
x.value()
, можно применить конструкции if (x) { }
и *x
. В типе std::optional
определено неявное преобразование к типу bool
и operator*
так, что работа с типом optional
похожа на работу с указателем.Существует еще один удобный вспомогательный оператор — это ->
struct Foo { int a; string b; }
и нужно получить доступ к одному из его членов с помощью переменной x
типа optional
, то можно написать конструкцию x->a
или x->b
. Конечно, сначала следует проверить, содержит ли х
значение. Если мы попробуем получить доступ к объекту типа optional
, который не содержит значения, то будет сгенерирована ошибка std::logic_error
. Таким образом, нельзя работать с большим количеством необязательных экземпляров, не проверяя их.С помощью блока try-catch
cout << "Please enter 3 numbers:\n";
try {
cout << "Sum: "
<< (*read_int() + *read_int() + *read_int())
<< '\n';
} catch (const std::bad_optional_access &) {
cout << "Unfortunately you did not enter 3 numbers\n";
}
Еще одним трюком для типа std::optional
optional::value_or
. Это поможет, когда мы хотим взять необязательное значение и, если оно окажется пустым, откатить его к значению, заданному по умолчанию. Можно решить эту задачу с помощью одной емкой строки x = optional_var.value_or(123)
, где 123
— значение по умолчанию.Применяем функции для кортежей
Начиная с C++11, STL предоставляет тип std::tuple
Однако иногда мы помещаем в кортеж значения, а затем хотим вызвать функции, передав в них его отдельные члены. Распаковывать члены по отдельности для каждого аргумента функции очень утомительно (а кроме того, могут возникнуть ошибки, если где-то вкрадется опечатка). Это выглядит так: func(get<0>(tup)
get<1>(tup)
, get<2>(tup), ...);
.Ниже мы рассмотрим, как упаковывать значения в кортежи и ловко распаковывать из них, чтобы вызвать функции, которые не знают о кортежах.
Как это делается
В этом примере мы реализуем программу, которая упаковывает значения в кортежи и распаковывает из них. Затем увидим, как вызывать функции, ничего не знающие о кортежах, и передавать в них значения из кортежей.
1. Сначала включим множество заголовочных файлов и объявим об использовании пространства имен std
#include
#include
#include
#include
#include
#include
using namespace std;
2. Определим функцию, которая принимает несколько параметров, описывающих студента, и выводит их на экран. Многие устаревшие интерфейсы и интерфейсы функций языка С выглядят похоже:
static void print_student(size_t id, const string &name, double gpa)
{
cout << "Student " << quoted(name)
<< ", ID: " << id
<< ", GPA: " << gpa << '\n';
}
3. В самой программе определим тип кортежа динамически и заполним его осмысленными данными о студентах:
int main()
{
using student = tuple
student john {123, "John Doe"s, 3.7};
4. Чтобы вывести такой объект на экран, можем разбить его на отдельные члены и вызвать функцию print_student
{
const auto &[id, name, gpa] = john;
print_student(id, name, gpa);
}
cout << "-----\n";
5. Создадим несколько студентов в виде списка инициализаторов для кортежей:
auto arguments_for_later = {
make_tuple(234, "John Doe"s, 3.7),
make_tuple(345, "Billy Foo"s, 4.0),
make_tuple(456, "Cathy Bar"s, 3.5),
};
6. Мы все еще можем относительно комфортно вывести их на экран, но, чтобы разбить кортеж на части, следует знать, сколько элементов в нем содержится. Если нужно писать подобный код, то понадобится также реструктурировать его в случае изменения интерфейса вызова функции:
for (const auto &[id, name, gpa] : arguments_for_later) {
print_student(id, name, gpa);
}