Span
Standardul C++20 introduce tipul std::span<T>, care permite referirea la orice secvență de valori de tip T – fie că este vorba despre std::vector<T>, std::array<T>, un tablou clasic sau alte secvențe de date. Să vedem care este avantajul său.
De exemplu, vrem să definim o funcție care calculează valoarea maximă dintr-un set de date. Dar pot exista mai multe tipuri de secvențe. Să presupunem că dorim ca funcția să funcționeze atât cu vectori, cât și cu tablouri. Am putea defini două versiuni ale funcției, care primesc separat un vector și un tablou:
int max(const std::vector<int>&);
int max(const int[], size_t);
Pentru a procesa un tablou, trebuie să transmitem și dimensiunea acestuia, pentru a putea fi parcurs într-o buclă. Însă std::span ne permite să scriem o singură funcție comună:
#include <iostream>
#include <vector>
#include <span>
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;
}
În acest caz, nu contează dacă transmitem un vector sau un tablou – funcția max() va funcționa cu ambele. Iar pentru tablouri, nu este necesar să transmitem dimensiunea – compilatorul o determină automat.
Funcții oferite de span
std::span oferă unele funcții similare cu cele din alte containere:
- size(): dimensiunea span-ului
- empty(): returnează true dacă span-ul este gol
- data(): pointer către elemente
- front(): primul element
- back(): ultimul element
Exemplu – dublăm valorile din span:
#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;
}
}
Crearea explicită a unui span
Un obiect std::span poate fi creat explicit, transmițându-i secvența dorită:
std::vector<int> numbers{1, 2, 3, 4, 5};
std::span<int> numSpan(numbers);
Totuși, deoarece std::span<T> presupune că putem modifica elementele sale, secvența transmisă nu trebuie să fie constantă. De exemplu, următorul cod va da eroare:
const std::vector<int> numbers{1, 2, 3, 4, 5};
std::span<int> numSpan(numbers); // ! Eroare
Pentru a lucra cu secvențe constante, trebuie să folosim forma std::span<const T>. Însă, în acest caz, nu vom putea modifica valorile elementelor:
#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"; // 1 2 3 4 5
}
std::cout << std::endl;
}