C++ std::span
Safe and efficient view over contiguous memory sequences
ð What is std::span?
std::span provides a safe, lightweight view over contiguous sequences like arrays and vectors. It offers bounds checking, size information, and modern C++ interface without owning the data it references.
// std::span example
#include <span>
#include <vector>
#include <iostream>
void print_data(std::span<int> data) {
std::cout << "Size: " << data.size() << std::endl;
for (int value : data) {
std::cout << value << " ";
}
}
// Works with arrays, vectors, etc.
Usage:
std::vector<int> vec = {1, 2, 3};
print_data(vec); // Size: 3, Values: 1 2 3
Key std::span Features
Safe Access
Bounds checking and size awareness
span.at(index); // Throws on bounds error
span[index]; // Fast access
Lightweight
Just pointer + size, no ownership
// No copying, just references
std::span<int> view = container;
Universal
Works with arrays, vectors, etc.
void func(std::span<int> data);
// Accepts any contiguous container
Subviews
Create views of parts of data
auto sub = span.subspan(2, 3);
auto first = span.first(5);
ðđ Basic std::span Usage
The C++ std::span provides a lightweight, non-owning view over a contiguous sequence of
elements, ideal for function parameters. It allows you to process arrays, vectors, or other containers
without copying data. For instance, calling a function with a span of 3 elements like {10, 20, 30} or 4
elements like {100, 200, 300, 400} lets the same function handle different sizes safely and
efficiently, enhancing code reusability and reducing overhead.
#include <span>
#include <vector>
#include <array>
#include <iostream>
void process_data(std::span<int> data) {
std::cout << "Processing " << data.size() << " elements: ";
for (int value : data) {
std::cout << value << " ";
}
std::cout << std::endl;
}
int main() {
// Works with different container types
std::vector<int> vec = {1, 2, 3, 4, 5};
std::array<int, 3> arr = {10, 20, 30};
int c_array[] = {100, 200, 300, 400};
process_data(vec); // Vector
process_data(arr); // Array
process_data(c_array); // C-style array
return 0;
}
Output:
Processing 5 elements: 1 2 3 4 5
Processing 3 elements: 10 20 30
Processing 4 elements: 100 200 300 400
ðđ Span Operations
std::span supports various operations to inspect and manipulate the underlying data
range. You can check properties like size (e.g., 10 elements) or emptiness (0 for false). It also
provides access to specific elements: first() and last() retrieve endpoints, while
operator[] accesses any index (like 4 at index 3). Subspans can be created to focus on a middle
section, such as elements 3 through 6, enabling flexible, bounds-checked data slicing.
#include <span>
#include <vector>
#include <iostream>
int main() {
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::span<int> span = data;
// Basic properties
std::cout << "Size: " << span.size() << std::endl;
std::cout << "Empty: " << span.empty() << std::endl;
// Element access
std::cout << "First: " << span.front() << std::endl;
std::cout << "Last: " << span.back() << std::endl;
std::cout << "At index 3: " << span[3] << std::endl;
// Subviews
auto first_half = span.first(5); // First 5 elements
auto last_half = span.last(5); // Last 5 elements
auto middle = span.subspan(2, 4); // 4 elements starting at index 2
std::cout << "Middle section: ";
for (int val : middle) {
std::cout << val << " ";
}
std::cout << std::endl;
return 0;
}
Output:
Size: 10
Empty: 0
First: 1, Last: 10, At index 3: 4
Middle section: 3 4 5 6
ðđ Fixed-Size Spans
Fixed-size spans (std::span<T, N>) enforce compile-time size checking for enhanced safety
and optimization. By specifying the exact number of elements (e.g., 3), the compiler can prevent size
mismatches and enable better performance. Examples include processing triples like {1, 2, 3},
{4, 5, 6}, or {7, 8, 9}. This is particularly useful in graphics, math libraries, or any
domain where data structure size is a critical, known invariant.
#include <span>
#include <array>
#include <iostream>
// Function expecting exactly 3 elements
void process_triple(std::span<int, 3> triple) {
std::cout << "Processing triple: ";
for (int val : triple) {
std::cout << val << " ";
}
std::cout << std::endl;
}
int main() {
std::array<int, 3> arr = {1, 2, 3};
int c_arr[3] = {4, 5, 6};
process_triple(arr); // â
Works - exact size
process_triple(c_arr); // â
Works - exact size
std::vector<int> vec = {7, 8, 9};
// Convert dynamic span to fixed-size span
std::span<int> dynamic_span = vec;
auto fixed_span = dynamic_span.first<3>();
process_triple(fixed_span); // â
Works
return 0;
}
Output:
Processing triple: 1 2 3
Processing triple: 4 5 6
Processing triple: 7 8 9