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

C++17 STL Стандартная библиотека шаблонов читать книгу онлайн
С++ — объектно-ориентированный язык программирования, без которого сегодня немыслима промышленная разработка ПО. В этой замечательной книге описана работа с контейнерами, алгоритмами, вспомогательными классами, лямбда-выражениями и другими интересными инструментами, которыми богат современный С++. Освоив материал, вы сможете коренным образом пересмотреть привычный подход к программированию.
Преимущество издания — в подробном описании стандартной библиотеки шаблонов С++, STL. Ее свежая версия была выпущена в 2017 году. В книге вы найдете более 90 максимально реалистичных примеров, которые демонстрируют всю мощь STL. Многие из них станут базовыми кирпичиками для решения более универсальных задач.
Вооружившись этой книгой, вы сможете эффективно использовать С++17 для создания высококачественного и высокопроизводительного ПО, применимого в различных отраслях.
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: