В этом посте мы рассмотрели использование std::visit в C++, мощной утилиты, позволяющей применять различные функции к вариантному объекту в зависимости от его текущего типа. std::visit может быть полезен в сценариях, где нам нужно обрабатывать варианты объектов с несколькими возможными типами, например, в синтаксическом анализаторе, где нам может потребоваться по-разному обрабатывать различные типы входных данных. Чтобы понять, чего достигает std::visit, мы рассмотрели альтернативный способ достижения той же функциональности, используя операторы if-else и перегрузку функций.

Простой пример

Вот простой типичный пример перебора массива вариантов 3 разных типов и вызова функций, перегруженных для каждого типа:

#include <iostream>
#include <variant>
#include <vector>

void func(int i) {
  std::cout << "Called func(int): " << i << std::endl;
}

void func(double d) {
  std::cout << "Called func(double): " << d << std::endl;
}

void func(const std::string& s) {
  std::cout << "Called func(string): " << s << std::endl;
}

int main() {
  std::vector<std::variant<int, double, std::string>> myVector = {1, 3.14, "Hello"};

  for (auto& element : myVector) {
    std::visit([](auto&& arg){ func(arg); }, element);
  }

  return 0;
}

В этом примере мы определяем три перегруженные функции: func(int), func(double) и func(const std::string&). Затем мы создаем вектор std::variant<int, double, std::string> и инициализируем его тремя элементами разных типов.

В цикле for мы используем std::visit для вызова соответствующей перегрузки функции для каждого элемента в векторе. std::visit принимает вызываемый объект в качестве своего первого аргумента, который в данном случае является лямбда-функцией, которая вызывает func со своим аргументом. Синтаксис auto&& в лямбда-функции говорит компилятору вывести тип аргумента и создать для него ссылку для пересылки, что позволяет нам вызывать func с правильной перегрузкой для каждого типа варианта.

std::visit — это полиморфная функция времени выполнения, которая определяет тип объекта варианта во время выполнения, а затем вызывает соответствующую перегрузку функции.

Когда мы вызываем std::visit, мы передаем ему вызываемый объект, который определяет набор перегруженных функциональных объектов для каждого типа в варианте. Во время выполнения std::visit использует тип объекта варианта, чтобы определить, какой объект функции вызывать, а затем передает объект варианта этому объекту функции.

Таким образом, в приведенном мной примере лямбда-функция, переданная std::visit, является объектом посетителя, который определяет набор перегруженных func функций для каждого типа в варианте. Во время выполнения std::visit определяет тип объекта варианта в векторе, а затем вызывает соответствующую функцию func для этого типа.

Как std::visit узнает тип объекта варианта?

std::visit использует информацию о типе, хранящуюся в самом объекте варианта, для определения его типа во время выполнения. Когда мы создаем вариантный объект, он содержит значение одного из своих альтернативных типов вместе с дискриминатором, указывающим, какой тип он имеет в настоящее время.
Вариантный объект хранит этот дискриминатор внутри, поэтому, когда мы вызываем std::visit для варианта объект, он проверяет этот дискриминатор, чтобы определить тип объекта.

Как только std::visit узнает тип объекта, он выбирает соответствующий функциональный объект для вызова из набора перегруженных функциональных объектов, предоставленных посетителем. Затем он вызывает этот функциональный объект, передавая вариантный объект в качестве аргумента.

Итак, в нашем примере, когда мы вызываем std::visit для каждого элемента в векторе, он проверяет дискриминатор вариантного объекта, чтобы определить его тип, а затем вызывает соответствующую функцию func для этого типа.

Этот эквивалентный код без использования перегрузок посещения и функции может объяснить, что std::visit делает концептуально.

#include <iostream>
#include <vector>
#include <variant>

void func(int i) {
    std::cout << "Called func(int): " << i << std::endl;
}

void func(double d) {
    std::cout << "Called func(double): " << d << std::endl;
}

void func(const std::string& s) {
    std::cout << "Called func(string): " << s << std::endl;
}

int main() {
    std::vector<std::variant<int, double, std::string>> vec = {1, 3.14, "hello"};

    for (const auto& variant : vec) {
        if (std::holds_alternative<int>(variant)) {
            int value = std::get<int>(variant);
            func(value);
        }
        else if (std::holds_alternative<double>(variant)) {
            double value = std::get<double>(variant);
            func(value);
        }
        else if (std::holds_alternative<std::string>(variant)) {
            std::string value = std::get<std::string>(variant);
            func(value);
        }
    }

    return 0;
}

В этом примере мы определяем три перегруженные функции: func(int), func(double) и func(const std::string&).

Затем мы создаем вектор вариантов объектов vec, который содержит по одному элементу каждого типа: int, double и std::string.

Затем мы перебираем каждый элемент в vec, используя std::holds_alternative для определения типа текущего варианта объекта. Мы используем std::get для извлечения значения вариантного объекта, а затем передаем это значение соответствующей функции в зависимости от его типа.

Краткое содержание

Таким образом, мы рассмотрели использование std::visit в C++, мощной утилиты, позволяющей применять различные функции к вариантному объекту в зависимости от его текущего типа. В целом, сообщение в блоге призвано помочь читателям понять основы std::visit и то, как его можно использовать для безопасного обхода вариантных объектов в C++.

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:

  • 👏 Хлопайте за историю и подписывайтесь на автора 👉
  • 📰 Смотрите больше контента в публикации Level Up Coding
  • 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
  • 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"

🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу