std::span în C++

O „fereastră” spre elemente consecutive din memorie (vector, array, tablou C), fără copiere și fără să le dețină.

Sintaxă
#include <span>

std::span<T> s(container);
std::span<const T> doar_citire(container);
std::span<T, N> dimensiune_fixa(tablou);

Ce face std::span

std::span ține doar un pointer și o lungime: vede elementele altui container, fără să le copieze și fără să le dețină. Este parametrul ideal pentru o funcție care trebuie să lucreze cu „un șir de elemente”, oricum ar fi stocate: aceeași funcție f(std::span<const int>) primește un std::vector<int>, un std::array sau un tablou C.

Cu std::span<const T> funcția doar citește; cu std::span<T> poate modifica elementele originale. first(n), last(n) și subspan(poz, n) dau bucăți din fereastră, tot fără copiere. Are size(), empty(), operator[], front, back, data și se poate parcurge cu for.

Pentru că nu deține memoria, un span este valid doar cât timp există containerul și cât timp acesta nu se realocă.

Parametri

Parametru Descriere
T Tipul elementelor (const T pentru acces doar la citire).
N Opțional: dimensiunea fixă, cunoscută la compilare.

Ce returnează

Un obiect span (o vedere asupra elementelor).

Exemple

Exemplul 1: O funcție pentru orice container continuu

#include <array>
#include <print>
#include <span>
#include <vector>

double medie(std::span<const double> valori) {
    double suma = 0;
    for (double v : valori) {
        suma += v;
    }
    return valori.empty() ? 0 : suma / static_cast<double>(valori.size());
}

int main() {
    std::vector<double> note{9.5, 8.0, 10.0};
    std::array<double, 4> temperaturi{12.0, 15.5, 11.0, 9.5};
    double tablou_c[] = {1.0, 2.0};

    std::println("{:.2f}", medie(note));
    std::println("{:.2f}", medie(temperaturi));
    std::println("{:.2f}", medie(tablou_c));
}

Rezultat

9.17
12.00
1.50

Exemplul 2: Bucăți dintr-un vector și modificare prin span

#include <print>
#include <span>
#include <vector>

void dubleaza(std::span<int> s) {
    for (int& x : s) {
        x *= 2;
    }
}

int main() {
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8};
    std::span<int> tot(v);
    std::println("primele 3:   {}", tot.first(3));
    std::println("ultimele 2:  {}", tot.last(2));
    std::println("subspan(2,4): {}", tot.subspan(2, 4));

    dubleaza(tot.subspan(4));      // modifică elementele originale, de la poziția 4
    std::println("vectorul:    {}", v);
}

Rezultat

primele 3:   [1, 2, 3]
ultimele 2:  [7, 8]
subspan(2,4): [3, 4, 5, 6]
vectorul:    [1, 2, 3, 4, 10, 12, 14, 16]

Greșeli frecvente

  • Creezi un span dintr-un vector temporar sau păstrezi un span după push_back. Span-ul indică memorie eliberată (comportament nedefinit).
  • Folosești std::span<int> ca parametru pentru o funcție care doar citește și nu poți trimite un const std::vector<int>. Folosește std::span<const int>.
  • Încerci să creezi un span dintr-un std::list sau std::deque. Span-ul cere elemente consecutive în memorie; aceste containere nu le au.

Comenzi înrudite

  • std::vector::data: Returnează un pointer la primul element al vectorului, pentru funcții care lucrează cu tablouri C.
  • std::array: Tablou cu dimensiune fixă, cunoscută la compilare, cu interfața unui container (size, at, begin, end).
  • std::vector: Tabloul dinamic din C++: ține elementele unul după altul în memorie și își mărește singur dimensiunea.
  • std::string_view: Vedere doar pentru citire asupra unui text existent, fără copiere; se poate micșora la capete.