STL <string_view> a Non-Owning String Class
string_view
is conceptually only a view of the string: usually implemented as[ptr, length]. When a string_view is created there's no need to copy the data (as opposede to when you create a copy of a string). What's more string_view is smaller than std::string - regarding the size on the stack/heap.
For example when we look at a possible (pseudo) implementation:
string_view { size_t _len; const CharT* _str; }
[you can tell that obviously] returning string views, creating string views, using std::string
. However, the initial performance tests showed that std::string
is usually highly optimized and sometimes string_view doesn't win that much.
To address the issue with std::string
being expensive to initialize (or copy), C++17 introduced std::string_view
(which lives in the <string_view> header). std::string_view
provides read-only access to an existing string (a C-style string, a std::string
, or another std::string_view
) without making a copy. Read-only means that we can access and use the value being viewed, but we can not modify it.
Look at the following example:
#include <iostream> #include <string_view> // C++17 // str provides read-only access to whatever argument is passed in void printSV(std::string_view str) // now a std::string_view { std::cout << str << '\n'; } int main() { std::string_view s{ "Hello, world!" }; // now a std::string_view printSV(s); return 0; }
This program makes no copies of the string Hello, world!
.
When we initialize std::string_view
with C-style string literal Hello, world!
, s provides read-only access to Hello, world!
without making a copy of the string. When we pass s to Hello, world!
through str, again without making a copy of the string.
std::string_view
over std::string
when you need a read-only string, especially for function parameters.std::string_view can be initialized with many different types of strings
One of the neat things about a std::string_view is how flexible it is. A std::string_view object can be initialized with a C-style string, a std::string, or another std::string_view:
#include <iostream> #include <string> #include <string_view> int main() { std::string_view s1 { "Hello, world!" }; // initialize with C-style string literal std::cout << s1 << '\n'; std::string s{ "Hello, world!" }; std::string_view s2 { s }; // initialize with std::string std::cout << s2 << '\n'; std::string_view s3 { s2 }; // initialize with std::string_view std::cout << s3 << '\n'; return 0; }
std::string_view
parameters will accept many different types of string arguments
Both a C-style string and a std::string
will implicitly convert to a std::string_view
. Therefore, a std::string_view
parameter will accept arguments of type C-style string, a std::string
, or std::string_view
:
#include <iostream> #include <string> #include <string_view> void printSV(std::string_view str) { std::cout << str << '\n'; } int main() { printSV("Hello, world!"); // call with C-style string literal std::string s2{ "Hello, world!" }; printSV(s2); // call with std::string std::string_view s3 { s2 }; printSV(s3); // call with std::string_view return 0; }
std::string_view
will not implicitly convert to std::string
Because std::string
makes a copy of its initializer (which is expensive), C++ won't allow implicit conversion of a std::string_view
to a std::string
. This is to prevent accidentally passing a std::string_view
argument to a std::string parameter
, and inadvertently making an expensive copy where such a copy may not be required.
However, if this is desired, we have two options:
std::string
with a std::string_view
initializer (which is allowed, since this will rarely be done unintentionally)std::string_view
to a std::string
using static_castThe following example shows both options:
#include <iostream> #include <string> #include <string_view> void printString(std::string str) { std::cout << str << '\n'; } int main() { std::string_view sv{ "Hello, world!" }; // printString(sv); // compile error: won't implicitly convert std::string_view to a std::string std::string s{ sv }; // okay: we can create std::string using std::string_view initializer printString(s); // and call the function with the std::string printString(static_cast<std::string>(sv)); // okay: we can explicitly cast a std::string_view to a std::string return 0; }
Assignment changes what the std::string_view
is viewing
Assigning a new string to a std::string_view
causes the std::string_view
to view the new string. It does not modify the prior string being viewed in any way.
The following example illustrates this:
#include <iostream> #include <string> #include <string_view> int main() { std::string name { "Alex" }; std::string_view sv { name }; // sv is now viewing name std::cout << sv << '\n'; // prints Alex sv = "John"; // sv is now viewing "John" (does not change name) std::cout << sv << '\n'; // prints John std::cout << name << '\n'; // prints Alex return 0; }
In the above example, sv = "John" causes sv to now view the string "John". It does not change the value held by name (which is still "Alex").
Literals for std::string_view
Double-quoted string literals are C-style string literals by default. We can create string literals with type std::string_view
by using a sv suffix after the double-quoted string literal. The sv must be lower case.
#include <iostream> #include <string> // for std::string #include <string_view> // for std::string_view int main() { using namespace std::string_literals; // access the s suffix using namespace std::string_view_literals; // access the sv suffix std::cout << "foo\n"; // no suffix is a C-style string literal std::cout << "goo\n"s; // s suffix is a std::string literal std::cout << "moo\n"sv; // sv suffix is a std::string_view literal return 0; }
It's fine to initialize a std::string_view
object with a C-style string literal (you don't need to initialize it with a std::string_view
literal).
That said, initializing a std::string_view
using a std::string_view
literal won't cause problems (as such literals are actually C-style string literals in disguise).
Finding and Checking Substrings in a std::string_view
Object
Since C++17, member functions std::basic_string_view<CharT,Traits>::find
and std::basic_string_view<CharT,Traits>::rfind
will return either the position of the first or last occurrence of a substring or npos
if not found.
starts_with(substr)
(C++20), starts_with(substr)
(C++20), and contains(substr)
(C++20) return a bool indicating whether *this
starts with, ends with or just contains substring substr.
constexpr std::string_view
Unlike std::string, std::string_view has full support for constexpr:
#include <iostream> #include <string_view> int main() { constexpr std::string_view s{ "Hello, world!" }; // s is a string symbolic constant std::cout << s << '\n'; // s will be replaced with "Hello, world!" at compile-time return 0; }
This makes constexpr std::string_view the preferred choice when string symbolic constants are needed.