How to Use absl::Span for Safer Array and Pointer Passing in C++
Use absl::Span<T> to replace raw pointer-size pairs with a lightweight, non-owning view that provides bounds-checked access and accepts any contiguous container without copying data.
The Abseil C++ library provides absl::Span as a safer alternative to passing raw pointers and lengths to functions. Defined in absl/types/span.h, this class template enables APIs that work seamlessly with std::vector, C arrays, absl::InlinedVector, and other contiguous sequences while preventing common pointer-size mismatches and out-of-bounds errors.
Core Design of absl::Span
absl::Span is a non-owning view that stores only a pointer (ptr_) and a length (len_). According to the class comment at line 20 of absl/types/span.h, the data it points to must outlive the Span instance. This design makes Span as lightweight as a raw pointer pair but with strict type safety and bounds checking.
The class distinguishes between mutable and const access through template specialization. Use absl::Span<const T> for read-only views and absl::Span<T> when elements need modification.
Constructing absl::Span Objects
The header provides multiple construction strategies starting at line 25, allowing implicit or explicit conversion depending on mutability requirements. Internal SFINAE utilities in absl/types/internal/span.h enable automatic detection of containers providing data() and size() methods.
From Pointer-Size Pairs
The explicit constructor Span(pointer, size) creates a span from a raw pointer and element count. This is the lowest-level constructor used when interfacing with C APIs.
int* data = new int[100];
absl::Span<int> span(data, 100);
From C Arrays and Containers
absl::Span accepts C arrays directly through a template constructor Span(T (&arr)[N]), deducing the size at compile time. For standard containers providing data() and size() methods, implicit conversion works for Span<const T>, while Span<T> requires explicit construction to prevent accidental mutation.
std::vector<int> vec = {1, 2, 3};
absl::Span<const int> read_only = vec; // implicit
absl::Span<int> mutable_span(vec.data(), vec.size()); // explicit
Factory Helpers: MakeSpan and MakeConstSpan
To avoid verbose template arguments, use the factory helpers declared around line 86 in absl/types/span.h. absl::MakeSpan deduces the element type automatically for mutable spans, while absl::MakeConstSpan creates read-only views.
int arr[5] = {1, 2, 3, 4, 5};
auto span = absl::MakeSpan(arr); // absl::Span<int>
auto cspan = absl::MakeConstSpan(arr); // absl::Span<const int>
Bounds-Checked Access and Sub-Views
Safe Element Access
The implementation provides two access modes with different safety guarantees. operator[] uses hardening assertions (defined in absl/base/internal/hardening.h) for debug builds, while at() performs runtime bounds checking and throws std::out_of_range on failure (see lines 33-45).
absl::Span<int> span = GetData();
int first = span[0]; // Hardened assert in debug
int safe = span.at(0); // Throws if out of range
Creating Sub-Views Without Copying
The member functions subspan, first, and last return new absl::Span instances referencing subsets of the original data (lines 44-97). These operations only adjust the internal pointer and length, performing no heap allocation or element copying.
absl::Span<int> data = GetLargeBuffer();
auto middle = data.subspan(10, 20); // Elements 10-29
auto head = data.first(5); // First 5 elements
auto tail = data.last(5); // Last 5 elements
Practical Usage Examples
The following examples demonstrate typical patterns for safer array passing in the abseil-cpp codebase.
Read-Only Function Parameters
Accepting absl::Span<const T> allows a single function to process vectors, arrays, or initializer lists without overloads.
#include "absl/types/span.h"
#include <vector>
#include <iostream>
void PrintSum(absl::Span<const int> values) {
int sum = 0;
for (int v : values) sum += v;
std::cout << "sum = " << sum << '\n';
}
// Works with any contiguous sequence
void Example() {
std::vector<int> vec = {1, 2, 3, 4};
PrintSum(vec); // Implicit conversion
int arr[3] = {5, 6, 7};
PrintSum(arr); // Array decay
PrintSum({8, 9, 10}); // Initializer list
}
Mutable Span Operations
When mutation is required, use absl::MakeSpan to obtain a mutable view, then modify elements safely.
void DoubleValues(absl::Span<int> values) {
for (int& v : values) v *= 2;
}
void Example() {
int arr[5] = {1, 2, 3, 4, 5};
auto span = absl::MakeSpan(arr);
span[0] = 10; // Safe indexed access
DoubleValues(span);
}
Working with Sub-Views
Sub-span operations enable zero-cost slicing of data ranges.
void ProcessChunk(absl::Span<const float> data) {
auto header = data.first(4); // First 4 elements
auto payload = data.subspan(4); // Remaining elements
// Process separately...
}
C++20 Ranges Interoperability
When compiling with C++20, absl::Span satisfies the std::ranges::view and std::ranges::borrowed_range concepts (lines 94-101 of absl/types/span.h). This enables seamless use with standard range algorithms and views without copying underlying data.
// C++20 only
#include <ranges>
#include <algorithm>
absl::Span<int> data = GetData();
auto result = data | std::views::filter([](int x) { return x > 0; });
Summary
- Use
absl::Span<const T>for read-only function parameters to accept any contiguous container without copying. - Prefer factory helpers
absl::MakeSpanandabsl::MakeConstSpanover explicit template arguments when constructing spans. - Ensure lifetime safety by guaranteeing the underlying data outlives the
Spaninstance, as the view stores only a pointer and length. - Leverage bounds checking through
at()for runtime safety oroperator[]for debug-only hardening viaabsl/base/internal/hardening.h. - Create zero-cost sub-views using
subspan,first, andlastto reference data slices without allocation.
Frequently Asked Questions
What's the difference between absl::Span and std::span?
absl::Span predates the C++20 standard and provides similar functionality with additional Abseil-specific hardening. While std::span is standard C++20, absl::Span includes hardened assertions via absl/base/internal/hardening.h and integrates with Abseil's type system. Codebases using Abseil typically prefer absl::Span for consistency across C++14, C++17, and C++20 projects.
Can I store an absl::Span as a class member?
Yes, but you must ensure strict lifetime management. Because absl::Span is non-owning and stores only ptr_ and len_ (as defined at line 20 of absl/types/span.h), storing it in a class creates a potential dangling reference if the underlying container is destroyed or reallocated. Only store spans when the data lifetime is guaranteed to exceed the class instance lifetime.
How do I handle null pointers with absl::Span?
absl::Span supports null pointers with zero length, with nullability annotations defined in absl/base/nullability.h. You can construct an empty span with absl::Span<T>(nullptr, 0) or simply absl::Span<T>(). The empty() method checks for zero length, and data() may return null for empty spans. Always verify !span.empty() before dereferencing span.data().
Is absl::Span compatible with C++20 ranges?
Yes. As implemented in lines 94-101 of absl/types/span.h, absl::Span satisfies both std::ranges::view and std::ranges::borrowed_range concepts when <ranges> is available. This allows absl::Span to work with standard range algorithms, views, and pipelines without copying data or modifying the Abseil library code.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →