<MyRusakov.ru />

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

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

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

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

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

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

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

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

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

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

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

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

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

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

std::span в C++20: безопасная передача массивов и векторов в функции

std::span в C++20: безопасная передача массивов и векторов в функции

Поисковый запрос, под который написана статья: std::span C++20 примеры. Если коротко, std::span — это «окно» на непрерывный участок памяти: массив, std::array, std::vector или часть контейнера. Он не владеет данными, не выделяет память и почти ничего не стоит по производительности, но делает интерфейсы функций заметно чище.

Проблема: указатель и размер легко перепутать

До C++20 часто писали так:

#include <iostream>

int sum(const int* data, std::size_t size) {
    int result = 0;
    for (std::size_t i = 0; i < size; ++i) {
        result += data[i];
    }
    return result;
}

int main() {
    int numbers[] = {1, 2, 3, 4};
    std::cout << sum(numbers, 4) << std::endl;
}

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

Что такое std::span

std::span<T> — это невладеющее представление непрерывной последовательности элементов типа T. Обычно внутри он хранит указатель на первый элемент и количество элементов. Для использования нужен заголовок <span>.

Главная идея: функция принимает не конкретно std::vector или C-массив, а любой непрерывный набор данных подходящего типа.

Базовый пример: одна функция для vector, array и C-массива

#include <array>
#include <iostream>
#include <span>
#include <vector>

int sum(std::span<const int> values) {
    int result = 0;

    for (int value : values) {
        result += value;
    }

    return result;
}

int main() {
    int cArray[] = {1, 2, 3};
    std::array<int, 3> stdArray = {4, 5, 6};
    std::vector<int> vector = {7, 8, 9};

    std::cout << sum(cArray) << std::endl;
    std::cout << sum(stdArray) << std::endl;
    std::cout << sum(vector) << std::endl;
}

Обратите внимание на std::span<const int>. Это значит: функция может читать элементы, но не может их изменять. Для большинства функций анализа, поиска, подсчёта и вывода это лучший вариант.

Когда нужен span без const

Если функция должна менять элементы, используйте std::span<int>. Например, обнулим отрицательные значения:

#include <iostream>
#include <span>
#include <vector>

void replaceNegativeWithZero(std::span<int> values) {
    for (int& value : values) {
        if (value < 0) {
            value = 0;
        }
    }
}

int main() {
    std::vector<int> numbers = {10, -3, 5, -1};

    replaceNegativeWithZero(numbers);

    for (int value : numbers) {
        std::cout << value << ' ';
    }
}

Здесь данные изменяются прямо внутри numbers, потому что span не копирует элементы, а ссылается на существующую память.

Передача части массива: first, last и subspan

std::span удобен, когда нужно работать не со всем контейнером, а с его фрагментом.

#include <iostream>
#include <span>
#include <vector>

void print(std::span<const int> values) {
    for (int value : values) {
        std::cout << value << ' ';
    }
    std::cout << std::endl;
}

int main() {
    std::vector<int> numbers = {10, 20, 30, 40, 50};
    std::span<int> view(numbers);

    print(view.first(3));       // 10 20 30
    print(view.last(2));        // 40 50
    print(view.subspan(1, 3));  // 20 30 40
}

Это часто чище, чем передавать индексы начала и конца в каждую функцию.

std::span против const std::vector&

Новички часто пишут функции так:

void print(const std::vector<int>& values);

Это нормально, если функция действительно должна работать только с std::vector. Но если ей достаточно просто пройтись по числам, лучше так:

void print(std::span<const int> values);

Второй вариант принимает vector, array, обычный массив и поддиапазоны. Интерфейс становится универсальнее без шаблонов и лишнего копирования.

Статический размер span

Обычно используют динамический размер: std::span<int>. Но можно указать размер на этапе компиляции:

#include <iostream>
#include <span>

void printRgb(std::span<const int, 3> rgb) {
    std::cout << "R=" << rgb[0] << std::endl;
    std::cout << "G=" << rgb[1] << std::endl;
    std::cout << "B=" << rgb[2] << std::endl;
}

int main() {
    int color[] = {255, 128, 0};
    printRgb(color);
}

std::span<const int, 3> говорит: функция ожидает ровно 3 элемента. Это полезно для координат, RGB-цветов, небольших фиксированных буферов.

Важные ошибки и ограничения

  • std::span не владеет данными. Он не продлевает жизнь массива или вектора.
  • Нельзя возвращать span на локальный контейнер. После выхода из функции данные будут уничтожены.
  • operator[] не проверяет границы. Всегда проверяйте size(), если индекс может быть неправильным.
  • span работает с непрерывной памятью. Он подходит для vector, array, C-массивов, но не для std::list или std::map.

Плохой пример:

#include <span>
#include <vector>

std::span<int> makeSpan() {
    std::vector<int> temp = {1, 2, 3};
    return temp; // Ошибка проектирования: temp скоро будет уничтожен
}

Такой span будет указывать на уже несуществующие данные. Возвращайте сам контейнер или храните данные снаружи.

Практические рекомендации

  • Для чтения используйте std::span<const T>.
  • Для изменения используйте std::span<T>.
  • Не сохраняйте span надолго в полях класса, если не контролируете время жизни данных.
  • Используйте subspan(), чтобы явно передавать часть данных вместо пары индексов.
  • Не применяйте span, если функция должна владеть данными. Тогда нужен std::vector, std::array или другой контейнер.

Мини-пример из реальной задачи

Допустим, нужно посчитать среднее значение только по части измерений:

#include <iostream>
#include <span>
#include <vector>

double average(std::span<const double> values) {
    if (values.empty()) {
        return 0.0;
    }

    double sum = 0.0;
    for (double value : values) {
        sum += value;
    }

    return sum / values.size();
}

int main() {
    std::vector<double> temperatures = {21.5, 22.0, 23.1, 24.0, 22.8};
    std::span<double> all(temperatures);

    std::cout << "Average: " << average(all.subspan(1, 3)) << std::endl;
}

Функция average не знает, откуда пришли данные: из вектора, массива или среза. Ей это и не нужно.

Итог

std::span в C++20 — хороший инструмент для функций, которые работают с последовательностью элементов, но не должны владеть памятью. Он делает код универсальнее, безопаснее по смыслу и понятнее, чем связка «указатель + размер».

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

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

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

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

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

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

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

  1. Кнопка:

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

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

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

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

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

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