📚 Hub Books: Онлайн-чтение книгРазная литератураC++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+
1 ... 79 80 81 82 83 84 85 86 87 ... 121
Перейти на страницу:
class="p1">  } 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<T>.

Когда мы получаем экземпляр подобного типа откуда бы ни было, нужно проверить, он пуст или же содержит значение. Здесь поможет функция optional::has_value(). Если она возвращает значение true, то можно получить доступ к этому значению. Это позволяет сделать вызов T& optional::value().

Вместо того чтобы всегда использовать конструкции if (x.has_value()) {...} и x.value(), можно применить конструкции if (x) {  } и *x. В типе std::optional определено неявное преобразование к типу bool и operator* так, что работа с типом optional похожа на работу с указателем.

Существует еще один удобный вспомогательный оператор — это ->. Если у нас есть тип struct Foo { int a; string b; } и нужно получить доступ к одному из его членов с помощью переменной x типа optional<Foo>, то можно написать конструкцию 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 numbersn";

}

Еще одним трюком для типа 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 <iostream>

#include <iomanip>

#include <tuple>

#include <functional>

#include <string>

#include <list>

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<size_t, string, double>;

  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);

  }

  cout << "-----n";

7. Можно сделать лучше. Даже не зная типов аргументов функции print_student или количества членов кортежа, описывающего студентов, можно направить содержимое кортежа непосредственно в функцию с помощью std::apply. Она принимает указатель на функцию или объект функции и кортеж, а затем распаковывает кортеж, чтобы вызвать функцию, передав в нее в качестве параметров члены кортежа:

  apply(print_student, john);

  cout << " n";

8. Конечно, все это прекрасно работает и в цикле:

  for (const auto &args : arguments_for_later) {

    apply(print_student, args);

  }

  cout << "-----n";

}

9. Компиляция и запуск программы покажут, что работают оба подхода, как мы и предполагали:

$ ./apply_functions_on_tuples

Student "John Doe", ID: 123, GPA:

1 ... 79 80 81 82 83 84 85 86 87 ... 121
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?