C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц
Книгу C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц читаем онлайн бесплатно полную версию! Чтобы начать читать не надо регистрации. Напомним, что читать онлайн вы можете не только на компьютере, но и на андроид (Android), iPhone и iPad. Приятного чтения!
Шрифт:
Интервал:
Закладка:
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. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
- 2. Просьба отказаться от оскорблений, угроз и запугиваний.
- 3. Просьба отказаться от нецензурной лексики.
- 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.
Надеемся на Ваше понимание и благоразумие. С уважением, администратор knigkindom.ru.
Оставить комментарий
-
Р.Д.У.22 август 02:17
...мне тоже понравился этот русский вестерн. И озвучено неплохо. Советую....
Силантьев Вадим – Засада
-
Гость Любовь21 август 20:01
Прочитала залпом.... интересный сюжет, история захватывает, плакала вместе с героями. спасибо автору за интересное...
Вернуть жену. Без права на прощение? - Ира Орлова
-
Ма21 август 02:06
Роман хороший, но очень топорный и поэтому скучноватый, все как будто поверхностно, акцент на работе героев - киллер и главбух, а...
Гектор - Ольга Дашкова
