CodeNebula
C++20stladvanced

std::span (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

Non-owning view over a contiguous sequence of elements, providing bounds-safe access without ownership semantics. Replaces the error-prone pattern of passing a pointer and a size as separate parameters.

Real-World Scenario

Pass a subset of a vertex buffer to a rendering function in a game engine without copying data or losing size information.

Quick Snippet

snippet.cpp
#include <span>
#include <vector>

void render(std::span<const float> vertices) {
    for (float v : vertices) { /* process vertex */ }
}

std::vector<float> buffer = {1.0f, 2.0f, 3.0f, 4.0f};
render(buffer);                          // entire buffer
render(std::span{buffer}.subspan(0, 2)); // first two elements

Full Example

span.cpp
// CodeNebula - C++20 Example: std::span Container
// Compile: g++ -std=c++20 -Wall -Wextra -o example span.cpp

// std::span is a lightweight, non-owning view over a contiguous sequence
// of objects. It can be created from arrays, vectors, or any contiguous
// range. It avoids copying and supports both static and dynamic extents.

#include <array>
#include <iostream>
#include <numeric>
#include <span>
#include <vector>

// Accept a span instead of a specific container type
void print_span(std::span<const int> data) {
    std::cout << "[";
    for (std::size_t i = 0; i < data.size(); ++i) {
        if (i > 0) std::cout << ", ";
        std::cout << data[i];
    }
    std::cout << "] (size=" << data.size() << ")\n";
}

// Compute sum using span
int sum(std::span<const int> data) {
    int total = 0;
    for (int v : data) total += v;
    return total;
}

// Modify elements through a mutable span
void double_values(std::span<int> data) {
    for (int& v : data) v *= 2;
}

// Static extent span: compile-time known size
void process_triple(std::span<const int, 3> triple) {
    std::cout << "Triple: " << triple[0] << ", "
              << triple[1] << ", " << triple[2] << "\n";
}

int main() {
    // Span from C-style array
    int arr[] = {1, 2, 3, 4, 5};
    std::cout << "From C array: ";
    print_span(arr);

    // Span from std::vector
    std::vector<int> vec = {10, 20, 30, 40, 50};
    std::cout << "From vector:  ";
    print_span(vec);

    // Span from std::array
    std::array<int, 4> stdarr = {100, 200, 300, 400};
    std::cout << "From array:   ";
    print_span(stdarr);

    // Sum via span
    std::cout << "\nSum of vec: " << sum(vec) << "\n";

    // Subspans
    std::span<int> full(vec);
    std::cout << "First 3:  ";
    print_span(full.first(3));
    std::cout << "Last 2:   ";
    print_span(full.last(2));
    std::cout << "Middle:   ";
    print_span(full.subspan(1, 3));

    // Mutable span modifies original data
    double_values(vec);
    std::cout << "\nAfter doubling: ";
    print_span(vec);

    // Static extent
    std::array<int, 3> tri = {7, 8, 9};
    process_triple(tri);

    // Span properties
    std::span<int> s(vec);
    std::cout << "\nSpan properties:\n";
    std::cout << "  size: " << s.size() << "\n";
    std::cout << "  size_bytes: " << s.size_bytes() << "\n";
    std::cout << "  empty: " << s.empty() << "\n";
    std::cout << "  front: " << s.front() << ", back: " << s.back() << "\n";

    return 0;
}

// Expected output:
// From C array: [1, 2, 3, 4, 5] (size=5)
// From vector:  [10, 20, 30, 40, 50] (size=5)
// From array:   [100, 200, 300, 400] (size=4)
//
// Sum of vec: 150
// First 3:  [10, 20, 30] (size=3)
// Last 2:   [40, 50] (size=2)
// Middle:   [20, 30, 40] (size=3)
//
// After doubling: [20, 40, 60, 80, 100] (size=5)
// Triple: 7, 8, 9
//
// Span properties:
//   size: 5
//   size_bytes: 20
//   empty: 0
//   front: 20, back: 100
Run command
g++ -std=c++20 -Wall -Wextra -o example span.cpp