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