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;
}
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.
Caveats when Using string_view
The potential risks involved with using string_view should also be mentioned:
-
Taking care of the (non)null-terminated strings:
string_viewmay not contain NULL at the end of the string. So you have to be prepared for such a case.- Problematic when calling functions like
atoiandprintf, that expect null-terminated strings - Conversion into
std::string
- Problematic when calling functions like
-
References and Temporary objects:
string_viewdoesn't own the memory, so you have to be very careful when working with temporary objects.- When returning
string_viewfrom a function - Storing
string_viewin objects or containers.
- When returning