6

It is a common mistake to initialize a std::string_view with a temporary std::string.

using namespace std::literals;

std::string_view sv1 = "foo" ; // good
std::string_view sv2 = "bar"s; // bad: "foo"s will expire

std::cout << sv1 << "\n"       // outputs foo
          << sv2 << "\n";      // undefined behavior

That's because "bar"s, the temporary std::string, is destroyed at the end of the full-expression.

But how about "foo"sv?

std::string_view sv3 = "baz"sv;

Of course this should work, because the suffix sv is otherwise useless. But how is this fundamentally different from "baz"s? In other words, why does the string introduced by "baz"sv not expire?

L. F.
  • 19,445
  • 8
  • 48
  • 82

1 Answers1

10

Why the declaration of sv2 is bad

Per [basic.string.literals]/1:

string operator""s(const char* str, size_t len);

Returns: string{str, len}.

In "foo"s, the string literal "foo" is used to initialize a temporary std::string. The characters are copied to the underlying array of the temporary std::string. std::string_view is a non-owning view, and sv2 points to the underlying array of the temporary std::string. After the temporary std::string is destroyed, sv2 keeps pointing to the (now expired) underlying array, and trying to output sv2 results in undefined behavior.

Why the declaration of sv3 is good

Per [string.view.literals]/1:

constexpr string_view operator""sv(const char* str, size_t len) noexcept;

Returns: string_­view{str, len}.

Therefore, the declaration of sv3 is equivalent to:1

std::string_view sv3{"baz", 3};

sv3 directly points to the string literal "baz". A string literal has static storage duration and does not expire.

1 There is some subtlety here. Copy elision may or may not apply here. Since string_views are non-owning, copying string_views does not introduce new temporary strings. Therefore, regardless of whether a copy is to take place, the state of sv3 is the same.

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • 3
    Even without copy elision `sv3` is safe, since there is no heap, nor stack, in sight. With no elision, a non-owning temporary is created with data from the executable itself, let's call it "global data". When it's pointer/size are copied to the `sv3` over the function boundary, the temporary is destroyed, but, since it was non-owning, no memory is released, as it always was part of a segment in the binary. So now `sv3` is a safe view into that memory. – Marcin Zdun May 18 '19 at 06:07
  • @MarcinZdun Yes. Copy elision is not the most important point here. The point is that the string view is initialized directly from the string literal instead of a temporary `std::string`. That said, it feels strange to disregard the fact that C++17 guarantees copy elision :) – L. F. May 18 '19 at 06:26
  • 3
    @L.F. I agree copy elision is nice, but you put so much emphasis on it that it is sounds like copy ellision is necessary to get "`sv3` directly points to the string literal `"baz"`" statement true. Currently you read: "Why the declaration of sv3 is good -> Therefore, by guaranteed copy elision (...)". So the follow up question would be "how about `string_view` copies?" We both know they point directly to static `"baz"` as well. I'd state "A string literal has static storage duration and does not expire." to anwer header question in the first place, then leave remark about copy elision. – R2RT May 18 '19 at 08:58
  • @R2RT Well, I partially agree with your comment. First, I was just analyzing the underlying process, and copy elision was one of the steps. We shouldn't discriminate on it just because it sounds fancy ;-) But you are right in that I am putting too much emphasis on it, and my wording suggests that the omission of copying is essential for `sv3` to work. I have updated my answer to remove this potential inaccuracy. – L. F. May 18 '19 at 09:04
  • 1
    Let me add: [implementations are allowed not to elide copies here](https://timsong-cpp.github.io/cppwp/n4659/class.temporary#3) (and do not if the function is not inlined) – Language Lawyer May 19 '19 at 12:51
  • @LanguageLawyer OK ... Now I see that I am completely wrong. Thank you for informing me of this! I have updated my answer to reflect that. – L. F. May 19 '19 at 12:56