31

By getting string_view in C++17 we got cheap method of passing both std::string and char* to functions that do not take ownership of the string and avoid making temporary copies. By using std::string passed by value and std::move we get explicit and fast passing of string ownership for both r-value and l-value references.

My question is: is there any benefit in using const std::string& as any function parameter in new C++ standard?

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
bartop
  • 9,971
  • 1
  • 23
  • 54
  • 13
    [How exactly is std::string_view faster than const std::string&?](https://stackoverflow.com/questions/40127965), [When would I pass const& std::string instead of std::string_view?](https://stackoverflow.com/questions/39564457), [Why only string view?](https://stackoverflow.com/questions/43023938), provide various information about that topic. As your question is close to those, you should mention what particular information is missing in those question and their answers. – t.niese Sep 16 '19 at 07:30
  • 4
    In one word: Null-termination. – Max Langhof Sep 16 '19 at 07:37

2 Answers2

33

Yes.

The problem with std::string_view is that it doesn't remember if it points to a null-terminated string or not.

If you're writing a wrapper for a C api that uses null-terminated strings, you would have to constantly copy your std::string_views into std::strings to make sure you have null-terminators.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 3
    Exactly. Properly written C++17 code should provide overloads for `const char *`, `const std::string &` and `std::string_view`. Especially since many parts of standard library didn't receive `string_view` support (exceptions, for example). –  Sep 16 '19 at 07:48
  • 19
    @StaceyGirl and here we go again with C++ complexity exploding in the face of the user – bartop Sep 16 '19 at 07:50
  • 4
    Also, I'm pretty sure `std::string_view` is slower if one worries so much about micro-performance. Not like it matters, but it really _ought to be_ slower. Passing a `string const&` will alias the string object which is a pointer in the worst case, and "no op" in the average case. Passing a `string_view` calls `operator string_view` to initialize a NRVO-copied `string_view` which initializes a pointer and a size. When given a `const char*` rather than a `string` it will call `strlen`, so... – Damon Sep 16 '19 at 15:42
  • 3
    @StaceyGirl Does that mean that many parts of the C++17 standard library, such as exceptions, are not properly written C++17 code? – wrtlprnft Sep 16 '19 at 19:35
  • 3
    @wrtlprnft Standard exceptions didn't receive `string_view` constructors and only have `const char *`/`const std::string &` ones, so if you want to pass a `string_view` from a derived class, you will have to create an additional string copy - something that `string_view` was supposed to avoid while reducing number of overloads. –  Sep 16 '19 at 19:40
  • 1
    does it mean that changing global constants from `const std::string{"..."}` to `const auto{"..."sv}` is always beneficial? – Noone AtAll Dec 21 '19 at 14:53
  • 1
    @NooneAtAll If you don't plan on passing those to functions with `const char *` or `const std::string &` parameters, then string view constants should be better, since they don't require heap allocations at startup. – HolyBlackCat Dec 21 '19 at 15:12
0

I know that this question is about C++17, but in C++20 I think that you can finally avoid using std::string& arguments by using C++20's concepts.

You can write a concept that requires that the argument type is convertible to an std::string_view and also convertible to either a const char* or an std::string, thus NULL-terminated. You can then convert the argument to a string_view in the function body, and you can safely use that string_view in C APIs, without doing runtime checks.

Here's an example from a small libcurl wrapper I maintain:

// NULL-terminated std::string_view
template<typename T>
concept Text = std::convertible_to<T, std::string_view> && (std::convertible_to<T, std::string> || std::convertible_to<T, const char*>);

void get(const std::string_view url, const Text auto... headers) noexcept {
    // ...
    struct curl_slist* curl_headers {nullptr};
    if constexpr (sizeof...(headers) != 0) {
        const std::array header_array {static_cast<std::string_view>(headers)...};
        for (const std::string_view header : header_array) {
            curl_headers = curl_slist_append(curl_headers, header.data());
        }
    }
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_headers);
    // ...
}
Tachi
  • 489
  • 8
  • 16