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

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

Книгу C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц читаем онлайн бесплатно полную версию! Чтобы начать читать не надо регистрации. Напомним, что читать онлайн вы можете не только на компьютере, но и на андроид (Android), iPhone и iPad. Приятного чтения!

1 ... 83 84 85 86 87 88 89 90 91 ... 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 ... 83 84 85 86 87 88 89 90 91 ... 121
Перейти на страницу:
Отзывы - 0

Прочитали книгу? Предлагаем вам поделится своим отзывом от прочитанного(прослушанного)! Ваш отзыв будет полезен читателям, которые еще только собираются познакомиться с произведением.


Уважаемые читатели, слушатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор knigkindom.ru.


Партнер

Новые отзывы

  1. Р.Д.У. Р.Д.У.22 август 02:17 ...мне тоже понравился этот русский вестерн. И озвучено неплохо. Советую.... Силантьев Вадим – Засада
  2. Гость Любовь Гость Любовь21 август 20:01 Прочитала залпом.... интересный сюжет, история захватывает, плакала вместе с героями. спасибо автору за интересное... Вернуть жену. Без права на прощение? - Ира Орлова
  3. Ма Ма21 август 02:06 Роман хороший, но очень топорный и поэтому скучноватый, все как будто поверхностно, акцент на работе героев - киллер и главбух, а... Гектор - Ольга Дашкова
Все комметарии
Новое в блоге