Adopting std::string_view for Read-Only String Parameters in C++17
Passing const std::string& to read-only functions forces heap allocations from const char* callers. std::string_view removes that cost, but its non-owning lifetime semantics demand strict discipline.
02 Mar 2026, 03:56 UTC

The allocation you didn't know you were paying for
Most C++ APIs accept read-only text as const std::string&. It feels efficient because no copy occurs when the caller already owns a std::string. The problem appears when the caller passes a string literal or a const char*: the compiler must construct a temporary std::string to bind to the reference. For strings longer than the implementation-defined small-string optimization threshold, that temporary triggers a heap allocation. In a hot logging loop or a parser that processes thousands of tokens, those hidden allocations add up.
How std::string_view avoids the copy
Introduced in C++17, std::string_view is a lightweight, non-owning view over a contiguous character sequence. Internally it stores only a pointer and a length. Because it does not own the underlying buffer, passing it by value is idiomatic and cheaper than passing const std::string& when ownership is unnecessary. It also converts implicitly from std::string and const char*, so callers using literals no longer force a temporary allocation.
The trade-off is lifetime. A std::string_view does not extend the life of the data it points to. It also is not guaranteed to be null-terminated, which matters when interfacing with C APIs.
A worked example and the lifetime trap
Consider a configuration lookup that only reads its argument:
#include <string_view>
#include <string>
// Before: const char* callers allocate a temporary std::string
std::string get_setting(const std::string& key) {
if (key == "timeout") return "30";
return "";
}
// After: no temporary allocation for const char* or std::string callers
std::string_view get_setting(std::string_view key) {
if (key == "timeout") return "30"; // safe: literal has static storage duration
return "";
}
Returning a view to a string literal is safe because literals persist for the entire program. The danger arises when the source is a local object:
std::string_view dangerous() {
std::string local = "temporary buffer";
return local; // ERROR: the view outlives the std::string
}
Implicit conversions can also create temporaries that vanish before the view is used:
std::string make_greeting();
std::string_view sv = make_greeting(); // dangling after this line
These mistakes typically compile without warnings. The resulting undefined behavior may appear to work until a refactor changes the allocation pattern.
Verifying safety with sanitizers
Before switching signatures in an existing codebase, compile a minimal example with C++17 enabled and sanitizers active. Run the following in your project build directory on Linux or macOS with GCC or Clang:
g++ -std=c++17 -Wall -Wextra -fsanitize=address,undefined -g sv_example.cpp -o sv_example
./sv_example
Expected check: the program exits normally with no AddressSanitizer or UndefinedBehaviorSanitizer diagnostics. A useful regression test creates a std::string in an inner scope, captures a std::string_view of it, then reads the view after the scope closes. With heap-use-after-free detection enabled, sanitizers should flag the invalid access.
Risk: sanitizers add significant runtime and memory overhead. Enable them in debug builds and continuous integration, not in production release builds.
When to keep std::string
std::string_view is not a universal replacement. If a function stores the text, mutates it, or returns newly created text, std::string or std::optional<std::string> remains correct because ownership and null termination matter more than allocation avoidance. Likewise, if the text must be passed to a C interface expecting const char*, keep const std::string& or explicitly construct a std::string before the call.
Actionable next step
Audit your public headers for read-only string parameters. If a function only reads the text and does not retain it, change const std::string& to std::string_view. Run your test suite with AddressSanitizer and UndefinedBehaviorSanitizer enabled to catch dangling views early. Keep std::string for anything that needs to own, mutate, or null-terminate its data.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.