为什么sv后缀引入的字符串不会过期?

Why does the string introduced by the sv suffix not expire?

用临时 std::string 初始化 std::string_view 是一个常见的错误。

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

那是因为"bar"s,临时的std::string,在完整表达式的末尾被销毁了。

但是 "foo"sv 呢?

std::string_view sv3 = "baz"sv;

当然这应该有效,因为后缀 sv 否则就没用了。但这与 "baz"s 有何根本区别?也就是说,为什么"baz"sv引入的字符串不会过期?

为什么 sv2 的声明是错误的

根据 [basic.string.literals]/1:

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

Returns: string{str, len}.

"foo"s中,字符串文字"foo"用于初始化临时std::string。字符被复制到临时std::string的底层数组中。 std::string_view是一个non-owning视图,sv2指向临时std::string的底层数组。临时 std::string 被销毁后,sv2 继续指向(现已过期)底层数组,并尝试输出 sv2 导致未定义的行为。

为什么sv3的声明是好的

根据 [string.view.literals]/1:

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

Returns: string_­view{str, len}.

因此,sv3的声明等价于:1

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

sv3直接指向字符串文字"baz"。字符串文字具有静态存储期限并且不会过期。

1 这里有一些微妙之处。复制省略 or may not 在此处应用。由于 string_views 是 non-owning,复制 string_views 不会引入新的临时字符串。因此,无论是否进行复制,sv3的状态都是一样的。