<MyRusakov.ru />

Создание нейросетей на Python

Создание нейросетей на Python

Этот курс научит Вас созданию нейросетей на Python. Курс рассчитан на начинающих. Вся информация идёт от простого к сложному очень маленькими шажками. При этом глубокое знание математики не требуется. Поскольку в курсе Вы будете получать эти знания по мере необходимости.

Из курса Вы узнаете всю необходимую теорию и терминологию. Научитесь создавать нейросети самых разных архитектур и обучать их. Также Вы создадите собственный фреймворк. Что очень важно проделать для грамотного использования того же PyTorch. Затем Вы изучите и сам PyTorch.

Помимо уроков к курсу идут упражнения для закрепления материала.

Ещё Вы получите Бонусы, дополняющие основной курс: "Распознавание изображений", "Анализ настроения по тексту отзыва", "Программирование на Python с Нуля до Гуру".

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

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

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

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

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

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

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

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

enum class в C++: безопасные перечисления, флаги и побитовые операции (понятное руководство)

enum class в C++: безопасные перечисления, флаги и побитовые операции (понятное руководство)

Если вы ищете понятное руководство по enum class в C++, вы на месте. В статье разберём, почему «классические» перечисления часто приводят к ошибкам, как enum class решает эти проблемы, а также покажем, как корректно работать с битовыми флагами и выводом перечислений на печать. Всё с практическими примерами и лучшими практиками для реального кода.

Почему enum class, а не старый enum

Классический enum в C++ «вываливает» имена в область видимости и неявно приводится к целым типам. Это удобно, но небезопасно. enum class (scoped enum) решает обе проблемы: значения находятся в собственной области и не приводятся к int без явного static_cast.

#include <iostream>

// Классический enum: имена попадают в общую область, неявно приводятся к int
enum ColorOld { Red, Green, Blue };

// Современный enum class: значения в своей области, нет неявных преобразований
enum class Color { Red, Green, Blue };

int main() {
    int x = Red;                 // ОК, но небезопасно: неявное приведение
    // int y = Color::Red;       // Ошибка: enum class не приводится к int автоматически

    Color c = Color::Green;      // Безопасно
    std::cout << x << "\n";       // 0
}

Резюме: используйте enum class по умолчанию — он делает код читаемее и предотвращает целый класс ошибок.

Базовый тип (underlying type) и размер

enum class позволяет явно указать базовый тип: это полезно для экономии памяти, серилизации и работы с протоколами. Чаще всего берут std::uint8_t или std::uint16_t для компактности.

#include <cstdint>
#include <type_traits>

enum class Status : std::uint8_t { Ok = 0, Warning = 1, Error = 2 };

static_assert(sizeof(Status) == 1, "Status должен занимать 1 байт");
static_assert(std::is_enum_v<Status>);

Совет: для флагов выбирайте степенную гранулярность (1 байт, 2 байта, 4 байта) — так проще работать с побитовыми операциями.

Печать и преобразования enum class

В отличие от обычного enum, значения enum class нельзя напрямую выводить в поток. Нужен явный static_cast<int> или собственная функция to_string/перегрузка operator<<.

#include <iostream>
#include <string>

enum class Color { Red, Green, Blue };

std::string to_string(Color c) {
    switch (c) {
        case Color::Red:   return "Red";
        case Color::Green: return "Green";
        case Color::Blue:  return "Blue";
    }
    return "Unknown"; // На случай добавления новых значений
}

std::ostream& operator<<(std::ostream& os, Color c) {
    return os << to_string(c);
}

int main() {
    Color c = Color::Blue;
    std::cout << c << "\n";                         // Blue
    std::cout << static_cast<int>(c) << "\n"; // 2
}

Совет: всегда делайте to_string для публичных перечислений. Это упростит логирование и отладку.

enum class как набор флагов: побитовые операции

Популярный сценарий: хранить несколько опций в одном числе с помощью битов. С enum class это безопасно, но нужно перегрузить побитовые операторы.

#include <cstdint>
#include <string>
#include <type_traits>

enum class FilePerm : std::uint8_t {
    None  = 0,
    Read  = 1 << 0, // 0b001
    Write = 1 << 1, // 0b010
    Exec  = 1 << 2  // 0b100
};

using FP_U = std::underlying_type_t<FilePerm>;

constexpr FilePerm operator|(FilePerm a, FilePerm b) {
    return static_cast<FilePerm>(static_cast<FP_U>(a) | static_cast<FP_U>(b));
}
constexpr FilePerm operator&(FilePerm a, FilePerm b) {
    return static_cast<FilePerm>(static_cast<FP_U>(a) & static_cast<FP_U>(b));
}
constexpr FilePerm operator~(FilePerm a) {
    return static_cast<FilePerm>(~static_cast<FP_U>(a));
}
constexpr FilePerm& operator|=(FilePerm& a, FilePerm b) {
    a = a | b; return a;
}
constexpr FilePerm& operator&=(FilePerm& a, FilePerm b) {
    a = a & b; return a;
}

constexpr bool has(FilePerm mask, FilePerm flag) {
    return (static_cast<FP_U>(mask) & static_cast<FP_U>(flag)) != 0;
}

std::string to_string(FilePerm p) {
    if (p == FilePerm::None) return "---";
    std::string s(3, '-');
    if (has(p, FilePerm::Read))  s[0] = 'r';
    if (has(p, FilePerm::Write)) s[1] = 'w';
    if (has(p, FilePerm::Exec))  s[2] = 'x';
    return s;
}

#include <iostream>
int main() {
    FilePerm p = FilePerm::Read | FilePerm::Write;
    std::cout << to_string(p) << "\n"; // rw-

    if (has(p, FilePerm::Write)) {
        std::cout << "Есть право записи\n";
    }

    p &= ~FilePerm::Write; // снимаем флаг записи
    std::cout << to_string(p) << "\n"; // r--
}

Советы по флагам:

  • Всегда указывайте базовый тип (чаще std::uint8_t или std::uint32_t).
  • Добавляйте None = 0 и используйте его как «пустую маску».
  • Перегружайте |, &, ~, |=, &= и используйте вспомогательную has для проверки флагов.
  • Храните флаги как enum class, а не как «голые» int — меньше шансов перепутать разнородные флаги.

switch с enum class: безопасно и наглядно

switch с enum class помогает не забыть кейсы. Рекомендуется обрабатывать все значения и по возможности избегать default (тогда компилятор сможет предупредить о пропущенных ветках).

#include <iostream>

enum class Status : unsigned {
    Ok, Warning, Error
};

const char* message(Status s) {
    switch (s) {
        case Status::Ok:      return "OK";
        case Status::Warning: return "Warning";
        case Status::Error:   return "Error";
    }
    return "Unknown"; // На случай будущих расширений
}

int main() {
    std::cout << message(Status::Warning) << "\n";
}

Интеграция со старыми API

Если библиотека ожидает int, приводите явно: static_cast<int>(myEnum). Для чтения из внешних данных (JSON, конфиги) используйте to_string и аккуратный парсинг с проверкой диапазона.

Частые ошибки и как их избежать

  • Пытаетесь сравнивать разные enum class — это ошибка по дизайну. Явно приводите к общему типу только если есть строгая необходимость (лучше — не смешивать вовсе).
  • Побитовые операции без перегрузок — не скомпилируется. Добавьте операторы один раз рядом с объявлением.
  • Забыли задать базовый тип для флагов — получите «внезапное» расширение до int и неожиданные размеры структур. Всегда фиксируйте underlying type.
  • Вывод в поток без to_string — будет непонятное число или ошибка компиляции. Перегрузите operator<< или используйте явный static_cast.

Рекомендуемые практики (чек-лист)

  • Используйте enum class по умолчанию; старый enum — только для совместимости.
  • Давайте осмысленные имена типу (Status, Color, FilePerm) и значениям (Read, Write), без префиксов типа STATUS_OK — область видимости решает конфликт имён.
  • Флаги — только с явным базовым типом и перегруженными побитовыми операциями.
  • Всегда обеспечивайте человекочитаемый вывод: to_string и/или operator<<.
  • Для публичных API документируйте диапазоны и «зарезервированные» значения, держите switch полным.

Итоги

enum class в C++ — это простой способ сделать код безопаснее: строгая типизация, отсутствие засорения пространства имен, явные преобразования и предсказуемые размеры. С ним удобно строить и обычные перечисления, и наборы битовых флагов. Возьмите примеры из статьи как шаблон для своего проекта — и вы быстро почувствуете разницу в качестве кода.

Хотите системно прокачать C++ от основ до уверенного уровня и дальше? Посмотрите программу и первые уроки курса: Практический курс «Программирование на C++ с Нуля до Гуру» — хорошее продолжение после этой статьи.

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

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

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

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

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

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

  1. Кнопка:

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

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

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

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

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

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