5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C++ STL basic_string_viewの使いたい場合、使えない場合

Last updated at Posted at 2020-06-04

C++17からSTLにstd::basic_string_viewが追加されました。これを使えば文字列やSTLコンテナのコピーによる無駄な演算やメモリ消費を防ぎ、さらに共通インターフェイスを提供できます。

std::basic_string_viewを使いたい場合

  1. 参照するデータの寿命がビューの寿命より長い場合。
  2. 文字列リテラル、STL文字列、文字型のSTLコンテナに共通インターフェイスを提供する場合。
  3. バイナリデータの一部を文字列として扱う場合。
  4. 関数の引数に使う場合。

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を使えない場合

  1. 参照するデータの寿命がビューの寿命より短い場合。
  2. 特に関数の戻り値から外れる場合。

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により一般的なデータの一部を扱うことができます。文字列ビューとしてのインターフェイスが不要ならこちらも使用できます。

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?