# How to Use absl::Span for Safer Array and Pointer Passing in C++

> Learn to use absl::Span in C++ for safer array and pointer passing. This lightweight view offers bounds-checked access and works with contiguous containers without data copies.

- Repository: [Abseil/abseil-cpp](https://github.com/abseil/abseil-cpp)
- Tags: how-to-guide
- Published: 2026-07-13

---

**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`](https://github.com/abseil/abseil-cpp/blob/main/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`](https://github.com/abseil/abseil-cpp/blob/main/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`](https://github.com/abseil/abseil-cpp/blob/main/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.

```cpp
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.

```cpp
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`](https://github.com/abseil/abseil-cpp/blob/main/absl/types/span.h). `absl::MakeSpan` deduces the element type automatically for mutable spans, while `absl::MakeConstSpan` creates read-only views.

```cpp
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`](https://github.com/abseil/abseil-cpp/blob/main/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).

```cpp
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.

```cpp
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.

```cpp
#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.

```cpp
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.

```cpp
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`](https://github.com/abseil/abseil-cpp/blob/main/absl/types/span.h)). This enables seamless use with standard range algorithms and views without copying underlying data.

```cpp
// 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::MakeSpan` and `absl::MakeConstSpan` over explicit template arguments when constructing spans.
- **Ensure lifetime safety** by guaranteeing the underlying data outlives the `Span` instance, as the view stores only a pointer and length.
- **Leverage bounds checking** through `at()` for runtime safety or `operator[]` for debug-only hardening via [`absl/base/internal/hardening.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/base/internal/hardening.h).
- **Create zero-cost sub-views** using `subspan`, `first`, and `last` to 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`](https://github.com/abseil/abseil-cpp/blob/main/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`](https://github.com/abseil/abseil-cpp/blob/main/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`](https://github.com/abseil/abseil-cpp/blob/main/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`](https://github.com/abseil/abseil-cpp/blob/main/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.