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 substr(...) is definitely much faster than deep copies of 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 printSV(), parameter str is initialized from s. This allows us to access Hello, world! through str, again without making a copy of the string.

Prefer 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:

Explicitly create a std::string with a std::string_view initializer (which is allowed, since this will rarely be done unintentionally) Convert an existing std::string_view to a std::string using static_cast

The 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.