<MyRusakov.ru />

Создание игр на Unreal Engine 5

Создание игр на Unreal Engine 5

Данный курс научит Вас созданию игр на Unreal Engine 5. Курс состоит из 12 модулей, в которых Вы с нуля освоите этот движок и сможете создавать самые разные игры.

В курсе Вы получите всю необходимую теоретическую часть, а также увидите массу практических примеров. Дополнительно, почти к каждому уроку идут упражнения для закрепления материала.

Помимо самого курса Вас ждёт ещё 8 бесплатных ценных Бонусов: «Chaos Destruction», «Разработка 2D-игры», «Динамическая смена дня и ночи», «Создание динамической погоды», «Создание искусственного интеллекта для NPC», «Создание игры под мобильные устройства», «Создание прототипа RPG с открытым миром» и и весь курс «Создание игр на Unreal Engine 4» (актуальный и в 5-й версии), включающий в себя ещё десятки часов видеоуроков.

Подробнее
Подписка

Подпишитесь на мой канал на YouTube, где я регулярно публикую новые видео.

YouTube Подписаться

Подписавшись по E-mail, Вы будете получать уведомления о новых статьях.

Подписка Подписаться

Добавляйтесь ко мне в друзья ВКонтакте! Отзывы о сайте и обо мне оставляйте в моей группе.

Мой аккаунт Мой аккаунт Моя группа
Опрос

Какая тема Вас интересует больше?

std::variant в C++17: безопасная альтернатива union с практическими примерами

std::variant в C++17: безопасная альтернатива union с практическими примерами

std::variant в C++17 — это контейнер из стандартной библиотеки, который в каждый момент времени хранит значение ровно одного типа из заданного списка. Проще говоря, переменная может быть либо int, либо std::string, либо, например, double, но не всем сразу.

Поисковый запрос, под который полезна эта статья: «std::variant C++17 примеры». Если вы уже знаете основы классов, функций и шаблонов, std::variant станет отличным следующим шагом к более безопасному современному C++.

Зачем нужен std::variant

Представьте, что программа читает настройки из файла. Значение настройки может быть числом, строкой или логическим значением:

  • max_users = 100
  • app_name = "Demo"
  • debug = true

Можно хранить всё строками, но тогда придётся постоянно вручную преобразовывать типы. Можно использовать наследование, но это часто слишком сложно для простой задачи. std::variant решает проблему аккуратно: список возможных типов известен заранее, а компилятор помогает не ошибиться.

Подключение и базовый пример

std::variant появился в C++17, поэтому компилировать код нужно с соответствующим стандартом: -std=c++17 или новее.

#include <iostream>
#include <string>
#include <variant>

int main() {
    std::variant<int, std::string, double> value;

    value = 10;
    value = std::string("Hello");
    value = 3.14;

    std::cout << "Индекс текущего типа: " << value.index() << std::endl;
}

Метод index() возвращает номер текущего типа в списке. Для std::variant<int, std::string, double>: int имеет индекс 0, std::string — 1, double — 2.

Как безопасно получить значение

Для чтения значения используется std::get. Но важно: если запросить не тот тип, будет выброшено исключение std::bad_variant_access.

#include <iostream>
#include <string>
#include <variant>

int main() {
    std::variant<int, std::string> data = std::string("C++");

    try {
        std::cout << std::get<std::string>(data) << std::endl;
        std::cout << std::get<int>(data) << std::endl; // ошибка
    } catch (const std::bad_variant_access& e) {
        std::cout << "Неверный тип внутри variant" << std::endl;
    }
}

Такой код работает, но в реальных проектах лучше сначала проверить тип.

Проверка типа через std::holds_alternative

std::holds_alternative<T> возвращает true, если внутри variant сейчас хранится тип T.

#include <iostream>
#include <string>
#include <variant>

int main() {
    std::variant<int, std::string, bool> setting = true;

    if (std::holds_alternative<bool>(setting)) {
        std::cout << "Boolean: " << std::get<bool>(setting) << std::endl;
    }

    setting = std::string("admin");

    if (std::holds_alternative<std::string>(setting)) {
        std::cout << "String: " << std::get<std::string>(setting) << std::endl;
    }
}

Это понятный вариант для новичков: проверили тип — получили значение.

std::get_if: получение без исключений

Если вы не хотите работать с исключениями, используйте std::get_if. Он возвращает указатель на значение или nullptr, если тип не совпал.

#include <iostream>
#include <string>
#include <variant>

int main() {
    std::variant<int, std::string> result = 404;

    if (int* code = std::get_if<int>(&result)) {
        std::cout << "Код ошибки: " << *code << std::endl;
    }

    if (std::string* text = std::get_if<std::string>(&result)) {
        std::cout << "Текст: " << *text << std::endl;
    } else {
        std::cout << "Это не строка" << std::endl;
    }
}

std::get_if часто удобен в прикладном коде: нет исключений, логика проверки явно видна.

std::visit: правильная обработка всех вариантов

Самый мощный способ работать с std::variantstd::visit. Он вызывает функцию для текущего значения, независимо от того, какого оно типа.

#include <iostream>
#include <string>
#include <variant>

int main() {
    std::variant<int, std::string, double> value = std::string("price");

    std::visit([](const auto& item) {
        std::cout << "Значение: " << item << std::endl;
    }, value);
}

Здесь лямбда принимает const auto&, поэтому подходит для любого типа внутри variant. Это удобно, когда для всех типов можно выполнить одно и то же действие, например вывести значение.

Практический пример: результат операции

Частая задача: функция должна вернуть либо успешный результат, либо ошибку. Для этого можно использовать std::variant.

#include <iostream>
#include <string>
#include <variant>

struct Error {
    std::string message;
};

using ParseResult = std::variant<int, Error>;

ParseResult parseNumber(const std::string& text) {
    try {
        return std::stoi(text);
    } catch (...) {
        return Error{"Не удалось преобразовать строку в число"};
    }
}

int main() {
    ParseResult result = parseNumber("123abc");

    if (int* number = std::get_if<int>(&result)) {
        std::cout << "Число: " << *number << std::endl;
    } else if (Error* error = std::get_if<Error>(&result)) {
        std::cout << "Ошибка: " << error->message << std::endl;
    }
}

В этом примере тип результата явно показывает: функция может вернуть либо int, либо Error. Это читается лучше, чем магические значения вроде -1 или пустой строки.

std::variant или union?

union тоже позволяет хранить разные типы в одной области памяти, но он гораздо менее безопасен. Программист сам должен помнить, какой тип сейчас активен. Ошибка легко приводит к неопределённому поведению.

std::variant хранит информацию об активном типе и предоставляет безопасные инструменты: std::get, std::get_if, std::holds_alternative, std::visit. Поэтому для обычного прикладного кода почти всегда лучше выбирать std::variant.

Типичные ошибки новичков

  • Запрашивать не тот тип через std::get. Если не уверены в типе, используйте std::holds_alternative или std::get_if.
  • Добавлять слишком много типов. Если в variant уже 10-15 вариантов, возможно, стоит пересмотреть архитектуру.
  • Использовать variant вместо обычной структуры. Если объект всегда содержит несколько полей одновременно, нужен struct, а не variant.
  • Путать variant и optional. std::optional<T> — это «значение есть или нет», а std::variant<A, B, C> — «значение одного из нескольких типов».

Рекомендации по использованию

  • Давайте variant осмысленное имя через using: например, using ConfigValue = std::variant<int, std::string, bool>;.
  • Для простого чтения используйте std::get_if — он безопасен и понятен.
  • Для общей обработки всех типов изучите std::visit.
  • Не используйте variant как способ спрятать плохую модель данных.

Если вы хотите уверенно разобраться не только с std::variant, но и с современным C++ в целом — от базового синтаксиса до продвинутых приёмов, обратите внимание на курс «Программирование на C++ с Нуля до Гуру». Он хорошо подойдёт тем, кто хочет учиться системно, а не собирать знания по разрозненным статьям.

Вывод

std::variant в C++17 — практичный инструмент для ситуаций, когда значение может иметь один из нескольких известных типов. Он безопаснее union, выразительнее набора флагов и часто проще и понятнее наследования. Начните с std::get_if и std::holds_alternative, а затем переходите к std::visit — так вы быстро научитесь применять std::variant в реальных программах.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (https://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: https://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: https://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как создать свой сайт

  2. Текстовая ссылка:

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (0):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.