Span

Последнее обновление: 07.03.2023

Стандарт C++20 предоставляет тип std::span<T>, который позволяет ссылаться на любую последовательность значений типа T - это может быть и std::vector<T>, и std::array<T>, и стандартный массив, и ряд других последовательностей. Рассмотрим, в чем его преимущество.

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

int max(const std::vector<int>&);
int max(const int[], size_t);

Для обработки массива также необходимо передать размер массива, чтобы перебрать его в цикле и найти максимальный элемент. Но тип std::span позволяет сократить код. Так, мы можем определить только одну версию:

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

// int max(const std::vector<int>&);
// int max(const int[], size_t);

int max(std::span<int>);


int main()
{
    std::vector<int> nums1{1, 2, 3, 4, 5};
    std::cout << max(nums1) << std::endl;   // 5

    int nums2[]{4, 5, 6, 7, 8};
    std::cout << max(nums2) << std::endl;   // 8
}

int max(std::span<int> data) 
{
    int result {data[0]};
    for (auto value : data)
    {
        if (result < value) result = value;
    }
    return result;
}

И здесь не важно, что мы передаем в функцию max() - вектор или массив, функция будет работать для обеих последовательностей. Причем для массива не надо передавать размер, компилятор определит его автоматически.

Также span имеет некоторые функции, которые имеются у других последовательностей:

  • size(): размер спана

  • empty(): возвращает true, если спан пуст

  • data(): указатель на элементы

  • front(): первый элемент

  • back(): последний элемент

Например, увеличим элементы спана в 2 раза:

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

void twice(std::span<int> data);


int main()
{
    std::vector<int> nums1{1, 2, 3, 4, 5};
    twice(nums1);
    for(const auto &n : nums1)
    {
        std::cout << n << "\t";   // 2       4       6       8       10
    }
    std::cout << std::endl;
}

void twice(std::span<int> items) 
{
    for (unsigned i{}; i < items.size(); i++)
    {
        items[i] *= 2;
    }
}

Объект std::span также можно создать явным образом, передав ему нужную последовательность:

std::vector<int> numbers{1, 2, 3, 4, 5};
std::span<int> numSpan(numbers);

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

const std::vector<int> numbers{1, 2, 3, 4, 5};
std::span<int> numSpan(numbers);  // ! Ошибка

Чтобы использовать константные последовательности, надо использовать форму std::span<const T>. Однако в этом случае мы не сможем менять значения элементов последовательности:

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

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

int main()
{
    const std::vector<int> numbers{1, 2, 3, 4, 5};
    std::span<const int> numSpan(numbers);
    print(numSpan);
}

void print(std::span<const int> items) 
{
    for(const auto &item : items)
    {
        std::cout << item << "\t";   // 2       4       6       8       10
    }
    std::cout << std::endl;
}
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850