Lesson 5 of 9
Lesson 5.4 established const std::string& as the default for string parameters. There's one
case it handles badly, and std::string_view fixes it.
The problem
#include <iostream>
#include <string>
std::size_t length_of(const std::string& text) {
return text.size();
}
int main() {
std::string owned = "evil genius";
std::cout << length_of(owned) << "\n";
std::cout << length_of("a literal") << "\n";
return 0;
}
11
9
Both work — but the second call allocates. "a literal" is a const char*, so a temporary
std::string is constructed (copying the characters to the heap) just so the reference has
something to bind to. Then it's destroyed. All to count characters that were already sitting in
the executable.
string_view is a pointer and a length
#include <iostream>
#include <string>
#include <string_view>
std::size_t length_of(std::string_view text) {
return text.size();
}
int main() {
std::string owned = "evil genius";
std::cout << length_of(owned) << "\n";
std::cout << length_of("a literal") << "\n";
std::cout << sizeof(std::string_view) << "\n";
return 0;
}
11
9
16
No allocation, either time. A string_view is just two things — where the characters start and
how many there are. 16 bytes, cheap to copy, so it's passed by value, not by reference.
It works with std::string, string literals, and character arrays alike, because all it needs is
a pointer and a length.
It's a view, not an owner
The name is the warning. A string_view doesn't own anything — it points into someone else's
characters. If they go away, it dangles:
std::string_view view;
{
std::string temp = "temporary";
view = temp; // view points into temp
} // temp destroyed here
std::cout << view; // undefined behaviour — dangling view
And the classic version, which looks completely reasonable:
std::string_view broken() {
std::string local = "hello";
return local; // returns a view of a string that's about to die
}
Same bug as returning a reference to a local (lesson 5.2), just harder to spot because the return
type doesn't contain a &.
The rule: use string_view for parameters; don't store one in a class member or return one
unless you can prove the underlying characters outlive it.
Cheap slicing
The other real win — taking a substring costs nothing:
#include <iostream>
#include <string>
#include <string_view>
int main() {
std::string text = "evil genius society";
std::string copied = text.substr(5, 6); // allocates
std::string_view viewed = std::string_view(text).substr(5, 6); // does not
std::cout << copied << "\n";
std::cout << viewed << "\n";
std::string_view rest = std::string_view(text).substr(12);
std::cout << rest << " (" << rest.size() << ")\n";
return 0;
}
genius
genius
society (7)
std::string::substr builds a new string with its own memory. string_view::substr just moves
the pointer and adjusts the length. Parsing a large document into fields, that difference is the
whole performance story.
The null-terminator caveat
A string_view is not guaranteed null-terminated — it's a pointer and a length, and its end
may be the middle of a longer string:
#include <iostream>
#include <string_view>
int main() {
std::string_view full = "evil genius";
std::string_view first = full.substr(0, 4);
std::cout << first << " (" << first.size() << ")\n";
// first.data() is NOT "evil" — it points at "evil genius"
return 0;
}
evil (4)
So there's no .c_str(). Passing .data() to a C function that expects a terminated string will
read past the end. If you need one, construct a std::string from the view first —
std::string(view) — and accept the copy.
Choosing
| Use | When |
| --- | --- |
| std::string | you own and store the text |
| std::string_view | a parameter you only read |
| const std::string& | a parameter, when you need .c_str() or an owning API |
For a read-only string parameter, std::string_view is the modern default. It accepts everything
const std::string& does, plus literals without allocating.
Try it
Write bool starts_with_word(std::string_view text, std::string_view word) reporting whether
text begins with word followed by either a space or the end of the string. Test with
"evil genius" against "evil", "evi", and "genius".
Checkpoint
std::string_viewis a non-owning pointer + length: cheap to copy, pass by value.- It avoids the temporary allocation
const std::string&causes for string literals. substron a view is free; on astd::stringit allocates.- A view does not own its characters — never return one or store one whose source may die.
- Views are not null-terminated, so there's no
.c_str(). - Default to
std::string_viewfor read-only string parameters.
Questions about this lesson
Log in to ask a question.