C++17: std::string_view

C++17 shipped with a very useful non-owning read-only string handling class template: std::basic_string_view<CharT, CharTraits> with several instantiations mirroring the std::basic_string class template: std::string_view, std::wstring_view, std::u8string_view, std::u16string_view, and std::u32string_view.

A std::string_view instance contains only a pointer to the first character in a char array and the length of the string. The std::string_view does not own such array and thus, it is not responsible for the lifetime of the text it refers to.

Since it contains the length of the string it represents, the string does not need to end with a '\0' character. This feature (the non-‘\0’ terminated string) enables a lot of interesting features like creating substring views by far faster than its std::string counterpart (since creating a substring means just updating the pointer and the length of the new std::string_view instance).

How a std::string_view is instantiated?

std::string_view today = "Monday";  // string_view constructed from a const char*
std::string aux = "Tuesday";
std::string_view tomorrow = aux;  // string_view constructed from a std::string
std::string_view day_after_tomorrow = "Wednesday"sv;  // string_view constructed from a std::string_view
constexpr std::string_view day_before_friday = "Thursday"; // compile-time string_view

How can I use a std::string_view?

A lot of classes from the C++ standard library have been updated to use the std::string_view as a first class citizen, so, for example, to print out a std::string_view instance, you could simply use the std::cout as usual:

std::string_view month = "September";
std::cout << "This month is " << month << "\n";

As I mentioned earlier, you can use the std::string_view to execute several actions in the underlying text with a better performance, for example, to create substrings:

#include <string_view>
#include <iostream>

int main()
{

    std::string_view fullName = "García Márquez, Gabriel";

    // I need to separate first name and last name

    // I find the index of the ","
    auto pos = fullName.find(",");
    if (pos == std::string_view::npos) // NOT FOUND
    {
        std::cerr << "Malformed full name\n";
        return -1;
    }

    std::string_view lastName = fullName.substr(0, pos);
    std::string_view firstName = fullName.substr(pos + 2);

    std::cout << "FIRST NAME: " << firstName;
    std::cout << "\nLAST NAME:  " << lastName << "\n";

    return 0;
}

To verify if a text starts or ends with something (starts_with() and ends_with() were introduced in C++20):

std::string_view price = "125$";
if (price.endsWith("$"))
{
    price.remove_suffix(1); // removes 1 char at the end of the string_view
    std::cout << price << " dollar\n";
}

It exposes a lot of functionality similar to the std::string class, so its usage is almost the same.

How can I get the content of a std::string_view?

  • If you want to iterate the elements, you can use operator[], at() or iterators in the same way that you do it in strings.
  • If you want to get the underlying char array, you can access to it through the data() method. Be aware the underlying char array can miss the null character as mentioned above.
  • If you want to get a valid std::string from a std::string_view, there is no automatic conversion from one to other, so you need to use a explicit std::string constructor:
std::string_view msg = "This will be a string";
std::string msg_as_string { msg };

std::cout << msg_as_string << "\n";

Handle with care

And though probably you are already imagining the scenarios a string_view can be used into your system, take into account that it could bring some problems because of its non-owning nature, for example this code:

std::string_view say_hi()
{
  std::string_view hi = "Guten Tag";
  return hi;
}

Is a completely valid and correct usage of a std::string_view.

But this slightly different version:

std::string_view say_hi()
{
  std::string_view hi = "Guten Tag"s;
  return hi;
}

Returns a corrupt string and the behavior for this is undefined. Notice the “s” at the end of the literal. That means “Guten Tag” will be a std::string and not a const char*. Being a std::string means a local instance will be created in that function and the std::string_view refers to the underlying char array in that instance. BUT, since it is a local instance, it will be destroyed when it goes out of scope, destroying the char array and returning a dangling pointer inside the std::string_view.

Sutile, but probably hard to find and debug.

To read more about std::string_view, consult the ultimate reference:

https://en.cppreference.com/w/cpp/string/basic_string_view