C++17からSTLにstd::basic_string_view
が追加されました。これを使えば文字列やSTLコンテナのコピーによる無駄な演算やメモリ消費を防ぎ、さらに共通インターフェイスを提供できます。
std::basic_string_view
を使いたい場合
- 参照するデータの寿命がビューの寿命より長い場合。
- 文字列リテラル、STL文字列、文字型のSTLコンテナに共通インターフェイスを提供する場合。
- バイナリデータの一部を文字列として扱う場合。
- 関数の引数に使う場合。
std::basic_string_view
は文字列リテラル、STL文字列(std::basic_string
)、文字型のSTLコンテナ(std::vector<char>
等)に対して、共通の文字列ビューを提供します。文字列リテラルの場合はstd::string_view_literals
名前空間のサフィックスsv
も使用できます。
#include <vector>
#include <string_view>
#include <iostream>
// サフィックスsvの使用に必要です。
using namespace std::string_view_literals;
int main()
{
std::wstring ws = L"ABCDE";
std::vector<wchar_t> v = {'A','B','C','D','E'};
// どの方法でも文字列ビューを作成できます。
std::wstring_view wsv1(L"ABCDE");
std::wstring_view wsv2 = L"ABCDE"sv;
std::wstring_view wsv3(ws);
std::wstring_view wsv4(v.data(), v.size());
// starts_withメンバ関数はC++20以降です。
std::wcout << wsv1.starts_with(L"ABC") << std::endl; // 1
std::wcout << wsv2.starts_with(L"ABC") << std::endl; // 1
std::wcout << wsv3.starts_with(L"ABC") << std::endl; // 1
std::wcout << wsv4.starts_with(L"ABC") << std::endl; // 1
return 0;
}
文字列やSTLコンテナのサイズが小さい場合はstd::basic_string
でも大差ありませんが、サイズが大きい場合はstd::basic_string_view
の使用が推奨されます。
例えば1 GBのテキストファイルをコピーするとき、std::basic_string
は回数分だけ1 GBのメモリ確保やコピーが発生します。こんなサイズは使わないと思いますが、100 MBの文字列を10回コピーすれば1 GBのコピー1回分です(メモリ使用量は使い方に依存します)。
また、バイナリデータ(バイト配列)として読み込んだファイルの一部を文字列として扱う場合にもstd::basic_string_view
が便利です。
std::basic_string_view
を使えない場合
- 参照するデータの寿命がビューの寿命より短い場合。
- 特に関数の戻り値から外れる場合。
std::basic_string_view
はアドレスとサイズしか保持しないので、参照元のデータが解放されると不正なメモリを参照します(ダングリングポインタ)。
#include <string>
#include <string_view>
#include <iostream>
// サフィックスsの使用に必要です。
using namespace std::string_literals;
std::wstring f()
{
std::wstring ws = L"ABCDE";
return ws;
}
int main()
{
// wsの寿命が先に終わるのでwsv1はダングリングポインタ
std::wstring_view wsv1;
{
std::wstring ws = L"ABCDE";
wsv1 = ws;
}
std::wcout << wsv1 << std::endl;
// fの戻り値(std::wstring型)が代入後に解放されて
// wsv2はダングリングポインタ
std::wstring_view wsv2 = f();
std::wcout << wsv2 << std::endl;
// L"ABCDE"s(std::wstring型)は代入後に解放されて
// wsv3はダングリングポインタ
std::wstring_view wsv3 = L"ABCDE"s;
// std::wstring_view wsv3 = std::wstring(L"ABCDE");
std::wcout << wsv3 << std::endl;
return 0;
}
追加情報
C++20からはstd::span
により一般的なデータの一部を扱うことができます。文字列ビューとしてのインターフェイスが不要ならこちらも使用できます。