コンテンツにスキップ

最終更新:2026-10-12

C++17 std::string_view

0.問題

string_viewはC++17以降で登場したもので、実体はleveldbにおけるsliceです。その目的は、メモリコピーの解消と効率的なsubstrにあります。

しかし、同時にポインタと参照の問題も持ち込まれました。string_viewは指す内容の所有権を持たないため、Rustと比べると、いわば借用にすぎません。内容を所有するオブジェクトが先に解放されると、ダングリング参照の問題が発生します。Rustのようにコンパイル時に変数のライフタイムを解析し、借用したリソースが使用中に解放されないことを保証しますが、C++にはそのような検査がないため、人手で保証する必要があります。

1.内部実装

string_viewはbasic_string_viewという型で、_Traitsにはデフォルトのchar_traitsが採用されていることがわかります。これは重要で、後述のいくつかの関数は、char_traitsのメンバ関数を使って核心的な操作を実行します。

template<class _CharT, class _Traits = char_traits<_CharT> >
    class _LIBCPP_TEMPLATE_VIS basic_string_view;

typedef basic_string_view<char>     string_view;

basic_string_viewの内部メンバ:

private:
    const   value_type* __data;
    size_type           __size;

コンストラクタはstringとconst char*に対応しています

constexpr basic_string_view() noexcept;
constexpr basic_string_view(const basic_string_view&) noexcept = default;
basic_string_view& operator=(const basic_string_view&) noexcept = default;
template<class Allocator>
constexpr basic_string_view(const charT* str);
constexpr basic_string_view(const charT* str, size_type len);

イテレータサポート:

constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr const_iterator cbegin() const noexcept;
constexpr const_iterator cend() const noexcept;
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator rend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;

その他の主要関数:

  • substr
  • remove_prefix
  • compare
  • find
  • ...

starts_with、ends_with、containsはC++20からサポートされます。この3つは、leveldbのsliceでは当初からサポートされていました。

char*のstring_viewリテラル:

constexpr basic_string_view<char>     operator "" sv( const char *str,     size_t len ) noexcept;
constexpr basic_string_view<wchar_t>  operator "" sv( const wchar_t *str,  size_t len ) noexcept;
constexpr basic_string_view<char16_t> operator "" sv( const char16_t *str, size_t len ) noexcept;
constexpr basic_string_view<char32_t> operator "" sv( const char32_t *str, size_t len ) noexcept;

2.使用上の罠

2.1 \0

string_viewは\0を考慮せず、実際の文字数のほうを気にします。

std::string_view s = "abc\0\0def"sv;
std::cout << "s: " << s.size() << " \"" << s << "\"\n";

出力:s: 8 "abcdef"

std::string_view str{"hello", 1};
std::cout << str.data() << std::endl; // 输出 hello,而不是h

ここではhelloと出力されることを期待していますが、実際に出力されるのはhです。

2.2 ライフタイム

string_view foo() {
    std::string s{"hello world"};
    return string_view{s};
}

この例では、sがスコープを抜けると、string_viewはダングリング参照になってしまいます。

ライフタイム

std::string_view は基となる文字列ストレージを所有しません。参照先オブジェクトのライフタイムが使用期間全体をカバーするように、必ず保証してください。

関連トピック

コメント