Стандарт 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;
}