How can I pass a substring by reference?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Passing a substring “by reference” is really a question about avoiding copies while still referring to part of an existing string. In C++, the right answer is usually not std::string&, because a substring is not a separate std::string object inside the original string. The practical tool is a non-owning view such as std::string_view, or a pair of iterators or indices when mutability matters.
Why std::string& Does Not Solve This
A reference such as std::string& binds to an actual std::string object. But text.substr(2, 5) produces a new string object by value, which means it copies characters.
This compiles and works, but it does not avoid allocation. The substring is a temporary string.
So if your real goal is “refer to a slice without copying,” you need a view, not a reference to a new string object.
Use std::string_view for Read-Only Substrings
For read-only access, std::string_view is usually the best tool.
This prints world without allocating a new string.
A cleaner pattern is to create a view over the whole string first and then slice the view:
That is efficient and expressive.
Lifetime Rules Matter
std::string_view does not own the characters it points to. That means the original string must stay alive and unchanged in ways that would invalidate the view.
This is safe:
This is dangerous:
The temporary string is destroyed immediately, leaving bad dangling. This is the main tradeoff of view-based substring passing.
If You Need to Modify the Original String
A substring view is not the right abstraction if the callee needs to mutate the original characters through that slice. In that case, pass the original string plus range information.
This changes the original string in place. There is no separate “substring reference” object in the standard library that behaves like a writable std::string& slice.
Iterators Are Another Option
For algorithms, iterator pairs can be a good fit:
This is often a good design when you want algorithm-style APIs that operate on ranges rather than string types specifically.
Choose the Tool Based on Intent
Use these rules:
- for read-only, zero-copy substring access, use
std::string_view - for writable access, pass the original string plus indices or iterators
- if ownership is required, accept a real
std::stringcopy
That is much clearer than trying to force every substring problem into “pass by reference” terminology.
Common Pitfalls
The biggest pitfall is assuming substr() returns a reference into the original string. It returns a new string object.
Another mistake is creating a std::string_view from a temporary string and then using it after the temporary has been destroyed.
Developers also sometimes use std::string_view for writable APIs. It is read-only by design.
Finally, if you need stable references across string mutation, remember that resizing or reallocation can invalidate views and iterators.
Summary
- A substring is not a built-in referenceable subobject of
std::string. - '
std::string_viewis the usual zero-copy solution for read-only substring access.' - For mutation, pass the original string with indices or iterators.
- '
substr()creates a new string and does not avoid copying.' - Always respect lifetime and invalidation rules when using views or iterators.

