string_view and span: non-owning views
Pass text and contiguous sequences efficiently using non-owning views while respecting object lifetime.
Learning objectives
- Explain why string_view and span do not own their elements.
- Pass text without requiring a std::string copy.
- Use span to describe a contiguous sequence.
- Identify dangling-view bugs.
Prerequisites: understand references, arrays, vectors, and object lifetime. Review References, Arrays, Vectors, and Scope and lifetime. This lesson uses std::string_view (C++17) and std::span (C++20); compile the complete examples in C++20 or later.
What is a view?
A view describes access to data owned somewhere else. It usually stores a pointer and a size rather than owning or copying the elements. This can make function interfaces flexible and efficient, but the owner must outlive the view.
std::string_view
#include <string_view>
#include <string>
#include <iostream>
void print_name(std::string_view name) {
std::cout << name << '\n';
}
int main() {
std::string text = "OpenLearn";
print_name(text);
print_name("C++");
}string_view can refer to a std::string, string literal, or another valid character sequence. It does not extend the lifetime of the source. Avoid returning a view to a local string.
Danger: returning a dangling view
std::string_view bad_name() {
std::string local = "temporary";
return local; // dangling after function returns
}The local string is destroyed at function exit. The returned view then refers to storage whose lifetime has ended. Return an owning std::string when the function creates the text.
std::span
#include <span>
#include <iostream>
void print_values(std::span<const int> values) {
for (int value : values) std::cout << value << ' ';
}
int main() {
int data[]{2, 4, 6};
print_values(data);
}span is a non-owning view over a contiguous sequence. It carries a pointer and a count. It can refer to arrays, vectors, and other contiguous storage. span<const int> allows reading values without modifying them through the span.
Bounds and lifetime
A span knows its size, but operator[] does not perform bounds checking. Use at() only if the span implementation/library version provides it (C++26 standard span adds at; ordinary C++20 span does not). In C++20, check indexes against size() yourself.
Choosing an interface
- Use
const std::string&when you need an owning string object without copying. - Use
std::string_viewfor read-only text that may come from different string-like sources. - Use
std::span<T>for a non-owning view over contiguous T elements. - Use an owning container when the function must retain the data beyond the caller's lifetime.
Practice
- Write a function that counts vowels in a string_view.
- Write a function that sums a span of integers.
- Explain why returning string_view to a local std::string is unsafe.
Show solutions
- Loop over characters in the view and count vowels.
- Loop over the span and accumulate into an integer.
- The local string is destroyed before the caller uses the returned view.
Summary
Views avoid ownership and copying but do not manage lifetime. Choose them for short-lived access to data whose owner is guaranteed to remain alive.