C++17: std::string_view

If you have to write a function that accepts a string, what type of parameter will you use? You have a couple of choices:

  • const std::string&: downside is that whenever you call the function, an std::string is expected. If you pass a const char*, an std::string instance will be created implicitly for you.
  • const char*: downside is that if your caller has an std::string, they will have to use c_str() on it to pass a const char* to your function.
  • A const char* overload and a const std::string& overload: downside is that you have to implement two functions. This solution quickly becomes unwieldy if you are writing a method that accepts more than one string as parameter. For example, suppose your method requires two strings as parameters. If you want to overload the method to provide all possible combinations of input argument types, then you end up writing four versions: (const char*, const char*), (const char*, const string&), (const string&, const char*), and (const string&, const string&). This only becomes worse with even more string parameters.

C++17 makes it easy by introducing a new type called std::string_view. From now on, if you are writing a function that accepts a string, use an std::string_view as parameter type. No need to use an std::string_view reference. A string_view is very cheap to copy, so it’s perfectly fine to pass by value. Basically, a string_view just contains a pointer to a string, and its length. A string_view parameter accepts any kind of string, such as a C++ std::string, a C-style const char* string, and a string literal, all without any copying involved!

std::string_view is defined in the <string_view> header. It has a similar interface as the well-known std::string class. However, since a string_view is a read-only view of a string, only the const operations of std::string are supported on a string_view. Additionally, a big advantage of a string_view is that it will never copy a string.

Here is a very brief example of its usage:

#include <iostream>
#include <string_view>

using namespace std;

void ProcessString(string_view myString)
{
    cout << myString; if (myString.size() >= 4)
    {
        cout << "   (Substring: " << myString.substr(2, 2) << ")";
    }
    cout << endl;
}

int main()
{
    string myString1 = "Hello";
    const char* myString2 = "C++";
    ProcessString(myString1);  // C++ string
    ProcessString(myString2);  // C-style string
    ProcessString("World!");   // String literal
}

The output of this piece of code is as follows:

Hello   (Substring: ll)
C++
World!   (Substring: rl)

My book, Professional C++, 4th Edition, explains the std::string_view class in a bit more details. Additionally, it explains all new C++17 features, and much more.

Share

5 Comments so far »

  1. Olivier Galibert said,

    Wrote on June 8, 2018 @ 8:44 pm

    Is that pointer/size 16 bytes combo significantly better than a 24-32 bytes SSO-ed string to we worth adding an indirection on access?

    OG.

  2. Marc Gregoire said,

    Wrote on June 9, 2018 @ 2:54 am

    Olivier: Maybe not, maybe yes. If performance is so critical for you that you start looking at the impact of an extra indirection, then you should really use a profiler and verify which is best for your specific use case.

  3. Mario said,

    Wrote on June 9, 2018 @ 1:44 pm

    Why would there be an extra level of indirection?

  4. Matthew said,

    Wrote on April 15, 2022 @ 8:36 pm

    Mario: When a string is small, the std::string contains the string itself instead a pointer to the string (elsewhere in memory). std::string_view always contains a pointer to the string. So, passing a std::string_view involves one level of indirection that passing a small std::string does not.

  5. Matthew said,

    Wrote on April 15, 2022 @ 8:50 pm

    _Always_ accepting a std::string_view in place of a std::string may not always be preferred. Suppose you are calling a third-party function within your own function that accepts a std::string. You cannot change the signature of the third-party function so you’ll have to copy the std::string_view into a new std::string to call it.

    What if that std::string_view started out as a std::string in the first place? You could’ve gotten away with 0 copies but putting std::string_view in the middle forced you to make one.

    If you’re just going to “inspect” a string within a function then I agree that passing by std::string_view is preferred.

    But if you know that you’re going to convert to std::string at some point (either for storage or as an argument to a 3rd party function) then it’s best to accept std::string.

Comment RSS · TrackBack URI

Leave a Comment

Name: (Required)

E-mail: (Required)

Website:

Comment: