Читать книги » Книги » Компьютеры и Интернет » Программирование » C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

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

Читать книгу C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц, Яцек Галовиц . Жанр: Программирование.
C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц
Название: C++17 STL Стандартная библиотека шаблонов
Дата добавления: 15 июль 2023
Количество просмотров: 1 473
(18+) Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних просмотр данного контента СТРОГО ЗАПРЕЩЕН! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту для удаления материала.
Читать онлайн

C++17 STL Стандартная библиотека шаблонов читать книгу онлайн

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

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

1 ... 85 86 87 88 89 ... 121 ВПЕРЕД
Перейти на страницу:
конструктору типа any достаточно информации о том, что мы собираемся создать. Второй параметр, {1,2,3}, — просто список инициализации, который будет передан в int_list, будучи встроенным в переменную типа any с целью создания объекта. Это способ избежать ненужного копирования или перемещения.

  print_anything(any(in_place_type_t<int_list>{}, {1, 2, 3}));

}

11. Компиляция и запуск программы дадут следующие результаты, они полностью соответствуют нашим ожиданиям:

$ ./any

Nothing.

It's a string: "abc"

It's an integer: 123

It's a list: 1, 2, 3,

It's a list: 1, 2, 3,

Как это работает

Тип std::any в чем-то похож на тип std::optional — он поддерживает метод has_value(), который говорит, содержит ли экземпляр значение. Но, помимо этого, он может содержать все что угодно, вследствие чего с ним работать немного сложнее, нежели с типом optional.

Прежде чем получать доступ к содержимому переменной типа any, нужно определить, какого типа хранящееся в ней значение, а затем преобразовать данные к этому типу.

Определить тип значения можно с помощью следующего сравнения: x.type() == typeid(T). Если оно возвращает результат true, то можно использовать преобразование any_cast, чтобы получить содержимое.

Обратите внимание: any_cast<T>(x) возвращает копию внутреннего значения. Если нужно получить ссылку, чтобы избежать копирования сложных объектов, то следует использовать конструкцию any_cast<T&>(x). Именно это мы и сделали, когда получали доступ к объектам типа string или list<int> в коде данного раздела.

 

 Если мы преобразуем экземпляр к неправильному типу, будет сгенерировано исключение std::bad_any_cast.

Хранение разных типов с применением std::variant

В языке С++ для создания типов можно использовать не только примитивы struct и class. Если нужно выразить, что какие-то переменные могут содержать значения типа А либо значения типа В (или C, или любого другого), то на помощь придут объединения. Проблема с объединениями заключается в том, что они не могут сказать, для хранения каких типов были инициализированы.

Рассмотрим следующий код:

union U {

  int a;

  char *b;

  float c;

};

void func(U u) { std::cout << u.b << 'n'; }

Допустим, мы вызовем функцию func для объединения, которое было инициализировано так, чтобы хранить в нем целое число в члене a. Тогда ничто не помешает нам получить доступ к нему так, как если бы оно было инициализировано способом, позволяющим хранить в нем указатель на строку в члене b. Из подобного кода могут появиться самые разнообразные ошибки. Прежде чем мы поместим в наше объединение вспомогательную переменную, которая скажет нам, для чего оно было инициализировано, можем воспользоваться типом std::variant, появившимся в C++17.

Тип variant, по сути, представляет собой обновленную версию типа union. Он не использует кучу, поэтому настолько же эффективно задействует память и время, как и решение, основанное на объединениях, так что нам нет нужды реализовывать его самостоятельно. Тип может хранить все что угодно, кроме ссылок массивов или объектов типа void.

В этом разделе мы создадим программу, которая задействует тип variant.

Как это делается

В этом примере мы реализуем программу, которая уже знакома с типами cat и dog и сохраняет смешанный список экземпляров обоих типов, не используя полиморфизм.

1. Сначала включим все необходимые заголовочные файлы и объявим об использовании пространства имен std:

#include <iostream>

#include <variant>

#include <list>

#include <string>

#include <algorithm>

using namespace std;

2. Далее реализуем два класса, имеющих схожий инструментарий, но не связанных друг с другом, что отличает их от классов, которые, скажем, наследуют от одного интерфейса или похожих интерфейсов. Первый класс — это класс cat. Объект класса cat имеет имя и может сказать «мяу» (meow):

class cat {

  string name;

public:

  cat(string n) : name{n} {}

  void meow() const {

    cout << name << " says Meow!n";

  }

};

3. Второй класс — это класс dog. Объект класса dog, конечно, может сказать не «мяу», а «гав» (woof):

class dog {

  string name;

public:

  dog(string n) : name{n} {}

  void woof() const {

    cout << name << " says Woof!n";

  }

};

4. Теперь можно определить тип animal, он будет представлять собой псевдоним типа std::variant<dog, cat>. По сути, он работает как старое доброе объединение, но имеет все дополнительные средства, предоставленные типом variant:

using animal = variant<dog, cat>;

5. Прежде чем писать основную программу, нужно реализовать два вспомогательных элемента. Одним из них является предикат animal. Вызвав is_type<cat>(...) или is_type<dog>(...), можно определить, какого типа данные содержатся в экземпляре типа animal. Реализация просто вызывает функцию holds_alternative, которая, по сути, является обобщенной функцией-предикатом для типа variant:

template <typename T>

bool is_type(const animal &a) {

  return holds_alternative<T>(a);

}

6. Вторым вспомогательным элементом является структура, которая ведет себя как объект функции. Это двойной объект функции, поскольку он дважды реализует оператор (). Одна из реализаций — перегруженная версия, принимающая экземпляры типа dog, вторая же принимает экземпляры типа cat. Для этих типов она просто вызывает функции woof или meow:

struct animal_voice

{

  void operator()(const dog &d) const { d.woof(); }

  void operator()(const cat &c) const { c.meow(); }

};

7. Воспользуемся результатами нашего труда. Сначала определим список переменных типа animal и заполним его экземплярами типов cat и dog:

int main()

{

  list<animal> l {cat{"Tuba"}, dog{"Balou"}, cat{"Bobby"}};

8. Теперь трижды выведем на экран содержимое списка, каждый раз новым способом. Один из них заключается в использовании variant::index(). Поскольку animal является псевдонимом для variant<dog, cat>, возвращаемое значение 0 означает, что переменная хранит экземпляр типа dog. Значение индекса 1 говорит о том, что это экземпляр типа cat. Здесь важен порядок типов в специализации variant. В блоке

1 ... 85 86 87 88 89 ... 121 ВПЕРЕД
Перейти на страницу:
Комментарии (0)