list
8. Теперь трижды выведем на экран содержимое списка, каждый раз новым способом. Один из них заключается в использовании variant::index()
animal
является псевдонимом для variant
, возвращаемое значение 0
означает, что переменная хранит экземпляр типа dog
. Значение индекса 1
говорит о том, что это экземпляр типа cat
. Здесь важен порядок типов в специализации variant
. В блоке switch case
мы получаем доступ к variant
с помощью вызова get
для получения экземпляра типа cat
или dog
, хранящегося внутри: for (const animal &a : l) {
switch (a.index()) {
case 0:
get
break;
case 1:
get
break;
}
}
cout << "-----\n";
9. Вместо того чтобы использовать численный индекс типа, можно также явно запросить каждый тип. Вызов get_if
do
на внутренний экземпляр типа dog
. Если такого экземпляра внутри нет, то указатель равен null
. Таким образом, мы можем попробовать получать разные типы до тех пор, пока не преуспеем. for (const animal &a : l) {
if (const auto d (get_if
d->woof();
} else if (const auto c (get_if
c->meow();
}
}
cout << "-----\n";
10. Последний — и самый элегантный вариант — это variant::visit
variant
. Объект функции должен реализовывать разные перегруженные версии для всех вероятных типов, которые может хранить variant
. Ранее мы реализовали структуру, имеющую необходимые перегруженные версии оператора ()
, поэтому можем использовать ее здесь: for (const animal &a : l) {
visit(animal_voice{}, a);
}
cout << "-----\n";
11. Наконец подсчитаем количество экземпляров типов cat
dog
в списке. Предикат is_type
может быть специализирован для типов cat
и dog
, а затем использован в комбинации с std::count_if
, чтобы получить количество экземпляров этого типа: cout << "There are "
<< count_if(begin(l), end(l), is_type
<< " cats and "
<< count_if(begin(l), end(l), is_type
<< " dogs in the list.\n";
}
12. После компиляции и запуска программы на экране будет список, выведенный три раза. Затем мы увидим, что предикаты is_type
count_if
, тоже работают хорошо:$ ./variant Tuba says Meow!
Balou says Woof!
Bobby says Meow!
-----
Tuba says Meow!
Balou says Woof!
Bobby says Meow!
-----
Tuba says Meow!
Balou says Woof!
Bobby says Meow!
-----
There are 2 cats and 1 dogs in the list.
Как это работает
Тип std::variant
std::any
, поскольку они оба могут содержать объекты разных типов, и нужно определять во время работы программы, что именно в них хранится, прежде чем получить доступ к их содержимому.С другой стороны, тип std::variant
std::any
тем, что мы должны объявлять, экземпляры каких типов он может хранить в виде списка шаблонных типов. Экземпляр типа std::variant
A
, B
или C
. Нельзя сделать так, чтобы в экземпляре типа variant
не хранился std::variant
не поддерживает возможность union U {
A a;
B b;
C c;
};
Проблема с объединениями заключается в том, что нужно создавать собственные механизмы для определения того, экземпляром какого типа оно было инициализировано: A
B
или C
. Тип std::variant
может сделать это за нас, не прилагая особых усилий.В коде, показанном в этом разделе, мы использовали три разных способа работы с содержимым переменной variant.
Первый способ — применение функции index()
variant
. Для типа variant
она может вернуть индекс 0
, если экземпляр был инициализирован переменной типа A
, 1
для типа B
или 2
для типа C
, и т.д. для более сложных вариантов.