はじめに
C++で、関数に文字列を渡す際、引数を const std::string&
とするのが一般的でした。しかし、この方法にはパフォーマンス上の問題があります。もし、関数にC言語スタイルの文字列リテラル(例: "Hello"
) を渡そうとすると、その場で一時的な std::string
オブジェクトが生成され、メモリ確保とコピーが発生してしまうのです。
この問題を解決するのが、C++17で導入された std::string_view
です。string_view
は、既存の文字列データの「一部分」または「全部」を指し示すだけの、所有権を持たない軽量なオブジェクトです。
この記事では、string_view
がどのようにして不要な文字列コピーを防ぎ、コードのパフォーマンスを向上させるのかを解説します。
【前提】C++17とは?
C++17は、2017年に正式化されたC++言語の規格です。std::string_view
はこのC++17で導入されたため、利用するにはC++17に対応したコンパイラが必要です。
string_view
を使ったサンプルコード
このコードは、文字列の一部を切り出して表示する関数を、従来の const string&
を使った場合と、string_view
を使った場合の2通りで定義し、その使われ方の違いを示します。
完成コード
#include <iostream>
#include <string>
#include <string_view> // string_view を使うために必要
using namespace std;
// 従来の const string& を使う関数
void printSubstring_String(const string& s) {
cout << s.substr(0, 5) << endl;
}
// C++17の string_view を使う関数
void printSubstring_StringView(string_view sv) {
cout << sv.substr(0, 5) << endl;
}
int main() {
string my_string = "Hello, Modern C++";
const char* c_style_string = "This is a C-style string";
cout << "--- const string& を使った場合 ---" << endl;
// string を渡す -> コピーは発生しない
printSubstring_String(my_string);
// C言語スタイル文字列を渡す -> 一時的なstringオブジェクトが生成され、コピーが発生
printSubstring_String(c_style_string);
cout << "\n--- string_view を使った場合 ---" << endl;
// string を渡す -> コピーは発生しない
printSubstring_StringView(my_string);
// C言語スタイル文字列を渡す -> コピーは発生しない
printSubstring_StringView(c_style_string);
// string_view を渡す -> もちろんコピーは発生しない
printSubstring_StringView(string_view{"This is a string_view literal"});
return 0;
}
コードの解説
void printSubstring_String(const string& s)
この従来の関数に、C言語スタイルの文字列 c_style_string
を渡すと、const string&
型の引数に合わせるために、一時的な std::string
オブジェクトがその場で生成されます。この時、文字列 "This is a C-style string"
を格納するためのメモリ確保と、文字のコピーが発生します。
void printSubstring_StringView(string_view sv)
この新しい関数は、引数として string_view
を受け取ります。string_view
は、std::string
からも、C言語スタイルの文字列リテラルからも、コピーなしで構築できます。
printSubstring_StringView(my_string)
:string_view
はmy_string
の内部バッファを直接指し示します。printSubstring_StringView(c_style_string)
:string_view
はc_style_string
のメモリアドレスを直接指し示します。
いずれの場合も、新たなメモリ確保や文字列のコピーは一切発生しません。string_view
は、ポインタと長さ情報を持つだけの非常に軽量なオブジェクトです。
string_view
の注意点
string_view
は、あくまで既存の文字列への「眺め」であり、データの所有権を持ちません。そのため、string_view
が指し示している元の文字列が、string_view
よりも先に破棄されてしまうと、「ダングリング(ぶら下がり)」状態になり、アクセスすると未定義の動作を引き起こします。
まとめ
今回は、C++17の std::string_view
を使って、不要な文字列コピーを回避し、パフォーマンスを向上させる方法を解説しました。
std::string_view
は、文字列の所有権を持たない、読み取り専用のビュー。- 関数の引数を
const std::string&
からstd::string_view
に変更することで、様々な種類の文字列をコピーコストなしで受け取れるようになる。 - 指し示している元の文字列の寿命には、常に注意を払う必要がある。
結論として、関数の引数で、文字列の所有権が必要なく、単に読み取るだけでよい場合は、常に const std::string&
よりも std::string_view
を使うのが、現代C++のベストプラクティスです。