A parser can inspect a piece of text without allocating a new string for every token. String_view makes that borrowing explicit by recording a pointer and length. It is convenient for short synchronous operations, but it does not keep the original characters alive. Returning a view into a temporary string creates a dangling result even though the result still reports a plausible length.
This lesson uses C++17. You will split a live owned string into views, change a view without changing the source, and create an owning copy when the result must survive source mutation. The examples never dereference a dangling view. Invalid code is discussed through its lifetime reasoning rather than executed to produce a misleading sample output.
A view describes characters owned elsewhere
Std::string owns its character sequence. Std::string_view borrows a contiguous sequence and is small enough to pass by value. Constructing a view over a string does not copy the characters. Constructing a string from a view explicitly creates owned text.
The view's size is the number of character elements in its sequence, not an automatic count of Unicode graphemes. For an ordinary char string holding UTF-8, a byte-oriented substring can cut through a multibyte character if the boundaries are not chosen carefully. The examples use ASCII protocol text so delimiter positions are byte boundaries with a clear meaning. A user-facing character operation needs an encoding-aware policy beyond string_view's storage abstraction.
The owner's lifetime must cover every use
A view into a local string cannot safely be returned after that local string is destroyed. A view into a temporary created during an expression can become invalid at the end of that expression. Binding the view does not extend the string's lifetime. A string literal has static storage, so a view into it has a different safe lifetime.
Mutation also matters. Growing or replacing a string can reallocate or otherwise invalidate its existing character references. Even when a particular implementation keeps an address unchanged, the logical content and validity contract may no longer match the view. Keep views within a borrowing operation, or document the owner's lifetime and mutation restrictions if they are stored. When retention is required, return an owning string or another appropriate owner.
View operations change the window
Removing a prefix or suffix adjusts the view's represented window. It does not erase the source characters. A substring produces another view into the same underlying storage. These operations are useful for parsing because they avoid repeated copying, but all resulting windows inherit the same owner dependency.
Validate offsets and lengths under the operation's rules. Some string_view operations throw for an invalid position, while others have preconditions rather than a general checked-access guarantee. Do not assume every member performs the same kind of validation. A parser should verify a delimiter exists before moving past it and should reject an oversized or malformed input under its format contract.
Data does not imply a null-terminated field
A view's data pointer identifies its first character, but the character after its represented sequence is not guaranteed to be a null terminator. A view into the middle of a larger string commonly ends before the owner's terminator. Passing only data to a C API that expects a null-terminated string can therefore read additional characters or beyond valid storage.
Use an API that accepts both pointer and length when available. If an API specifically requires an owned null-terminated string, construct a std::string and pass its c_str while that string remains alive. Also account for embedded null characters: an owning string can contain them, and a C API may stop earlier than the C++ length. The format boundary needs a deliberate policy rather than an assumption based on data's pointer type.
Choose borrowed parameters and owned results deliberately
A synchronous read-only function is often a good fit for a string_view parameter. It can inspect a literal, string or existing view without requiring a new owned copy. A service that queues the text for later work must establish ownership before the caller's source can disappear.
Avoid storing a view merely because copying seems expensive. First decide whether the data must survive, then measure if copying is actually significant. For repeated tokenization of a stable buffer, views can be an excellent design when the buffer owner and token lifetimes are clear. Tests should exercise literal input, live string input, empty fields and source mutation after an owning result is created. A sanitizer can expose some lifetime errors, but an unreported test run does not prove every dangling case safe.
Official references
C++ String View Lifetimes Examples
Each example shows the code, the output it produces and what each part does.
Split a live owned string into views
This complete program finds one delimiter, forms two views, and adjusts one view. The owning string remains alive and unchanged throughout the operation.
Only the view's window changes. A production parser may forbid empty labels or values and should enforce those domain rules after identifying the delimiter.
#include <iostream>
#include <string>
#include <string_view>
int main() {
std::string source = "item:bolt";
std::string_view whole(source);
std::size_t separator = whole.find(':');
if (separator == std::string_view::npos) {
std::cout << "Missing separator\n";
return 0;
}
std::string_view label = whole.substr(0, separator);
std::string_view value = whole.substr(separator + 1);
std::cout << label << '=' << value << '\n';
value.remove_prefix(1);
std::cout << "Adjusted view: " << value << '\n';
std::cout << "Owner: " << source << '\n';
}item=bolt
Adjusted view: olt
Owner: item:boltC++ String View Lifetimes in Real Projects
Worked exercise: return retained text as a copy
The task is to extract the value from code=BOLT and keep it after the source is cleared. The function accepts a borrowed view for input but returns an optional owning string. It rejects an empty or wrong key. This complete design makes retention independent of the original storage.
#include <iostream>
#include <optional>
#include <string>
#include <string_view>
std::optional<std::string> read_code(std::string_view input) {
if (input.size() > 100)
return std::nullopt;
std::size_t separator = input.find('=');
if (separator == std::string_view::npos ||
input.substr(0, separator) != "code")
return std::nullopt;
std::string_view value = input.substr(separator + 1);
if (value.empty())
return std::nullopt;
return std::string(value);
}
int main() {
std::string source = "code=BOLT";
auto code = read_code(source);
source.clear();
if (code)
std::cout << *code << '\n';
std::cout << "Source length: " << source.size() << '\n';
}BOLT
Source length: 0The copy is intentional because the caller retains the result. Returning the local value view instead would leave a dependency on source storage and mutation. The input-size bound is a format limit, not a guarantee about every possible downstream use.
C++ String View Lifetimes FAQ
Does string_view extend a temporary string's lifetime?
No. A view records an address and length without owning the string or extending its lifetime. A temporary string can be destroyed at the end of the expression, leaving the view dangling. Return an owning string when the result must survive. A view of a literal is different because the literal's storage persists, but that special case does not make all view constructions safe.
Does remove_prefix modify the original string?
It changes the view's starting position and size. The underlying characters remain unchanged. Every resulting view still depends on the same owner. Validate the requested removal under the member's preconditions and keep the owner alive. If the actual goal is to delete text from an owning string, use an appropriate string operation and review whether that mutation invalidates existing views.
Can I pass data to a null-terminated C API?
Only when you have separately established that the represented sequence has the required termination and content contract. A substring view generally does not end at a terminator. Prefer a pointer-and-length API or create an owning string whose c_str remains alive during the call. Embedded nulls need their own rule because C-style consumers can stop before the C++ sequence ends.
When is copying the better design?
Copy when the result must outlive the source, cross an asynchronous boundary, or remain stable while the source changes. The extra ownership is often simpler and safer than maintaining a hidden lifetime relationship. Borrowing is valuable for immediate inspection of stable data. Measure realistic costs rather than accepting a dangling or fragile design merely to avoid an allocation.
C++ String View Lifetimes Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
A parsed code must remain valid after the input string is cleared. Which return type fits?
C++ String View Lifetimes Quiz
2 questions to check what you learned.
What does string_view own?
- No character storage; it borrows a sequence.
- The original string and its allocator.
- A copied null terminator for every substring.
- A shared reference count to every source.
A substring view is passed to a C function that receives only data(). What is the concern?
- data() necessarily points to a new, terminated substring allocation.
- The view's end may not be null-terminated under the function's contract.
- The original string's terminator must occur at the view's end.
- remove_suffix writes a new terminator at the shortened boundary.