インターフェイスでstring_viewを使用する必要があるのはいつですか?


16

私は模倣Aに設計された内部ライブラリを使用しているC ++ライブラリを提案し、いつか過去数年間で、私はそのインターフェースが使用してから変更を参照std::stringしますstring_view

そこで、新しいインターフェースに適合するようにコードを忠実に変更しました。残念ながら、私が渡さなければならないのは、std :: stringパラメーターと、std :: string戻り値です。そのため、私のコードは次のように変更されました。

void one_time_setup(const std::string & p1, int p2) {
   api_class api;
   api.setup (p1, special_number_to_string(p2));
}

void one_time_setup(const std::string & p1, int p2) {
   api_class api;
   const std::string p2_storage(special_number_to_string(p2));
   api.setup (string_view(&p1[0], p1.size()), string_view(&p2_storage[0], p2_storage.size()));
}

私は、この変更がAPIクライアントとして私に何を買ったのか、実際には、コードを増やすこと以外は見当たらないでしょう(おそらく失敗するためです)。API呼び出しの安全性は低下し(APIがパラメーターのストレージを所有しなくなったため)、おそらくプログラム0の作業を節約しました(コンパイラーができる最適化の移動により)。起動後またはどこかで大きなループで行われない、または行われない割り当てがいくつかあります。このAPI用ではありません。

ただし、このアプローチは、他の場所で見られるアドバイス、たとえば次の回答に従うようです。

余談ですが、C ++ 17以降では、std :: string_viewを優先してconst std :: string&を渡すことは避けてください。

主に最適化の目的で、比較的安全なオブジェクトを安全性の低いオブジェクト(基本的には栄光のポインタと長さ)に普遍的に置き換えることを提唱しているように見えるため、このアドバイスは驚くべきものです。

だから、ときなければならない string_view使用すること、そしてときにそれをべきではないのですか?


1
std::string_viewコンストラクタを直接呼び出す必要はありませんstd::string_view。直接取得するメソッドに文字列を渡すだけで、自動的に変換されます。
ムゲッツ

@Mgetz-うーん。私は(まだ)本格的なC ++ 17コンパイラを使用していないので、おそらくそれが問題のほとんどです。それでも、ここのサンプルコードは、少なくとも1つを宣言するときに、その必要性を示しているように見えます。
TED

4
変換演算子が<string>ヘッダーにあり、自動的に発生する私の答えを参照してください。そのコードは欺くと間違っています。
ムゲッツ

1
「安全性の低いもの」スライスは文字列参照よりも安全性が低いのですか?
CodesInChaos

3
@TED呼び出し元は、参照が指している文字列を、スライスが指しているメモリを解放できるのと同じくらい簡単に解放できます。
CodesInChaos

回答:


18
  1. 値を取る機能は文字列の所有権を取得する必要がありますか?その場合は、std::string(非定数、非参照)を使用します。このオプションを使用すると、呼び出し側のコンテキストで再び使用されないことがわかっている場合、値を明示的に移動することもできます。
  2. 機能は文字列を読み取るだけですか?使用もしそうならstd::string_view(定数、非参照)ので、これはstring_view扱うことができるstd::stringchar*簡単に問題なく、コピーをせずに。これにより、すべてのconst std::string&パラメーターが置き換えられます。

最終的には、あなたのようにコンストラクタを呼び出す必要はないはずstd::string_viewです。変換を自動的に処理std::stringする変換演算子があります。


1つのポイントを明確にするために、このような変換演算子は、呼び出しの長さ全体にわたってRHS文字列値を維持することにより、最悪のライフタイムの問題も処理すると考えています。
TED

3
@TED値を読み取っているだけの場合、値は呼び出しよりも長くなります。所有権を取得している場合は、通話よりも長持ちする必要があります。したがって、なぜ両方のケースに対処したのか。変換演算子は、std::string_view使いやすくすることだけを扱います。開発者がプロ​​グラミングエラーである所有状況で誤って使用した場合。std::string_view完全に非所有者です。
ムゲッツ

なんでconst, non-ref?constであるパラメーターは特定の用途に応じて異なりますが、一般的には非constとして妥当です。そして、あなたは逃した3受け入れることができますスライス
v.oddouを

const std::string_view &代わりに渡す問題は何const std::string &ですか?
ceztko

@ceztkoは完全に不要であり、データにアクセスするときに余分な間接性を追加します。
Mgetz

15

A std::string_viewconst char*C ++ にaの利点のいくつかをもたらしますstd::string。string_view とは異なり

  • メモリーを所有していない、
  • メモリを割り当てません。
  • あるオフセットで既存の文字列を指すことができます。
  • ポインタ間接参照のレベルは、aより1つ少ないstd::string&

つまり、string_viewは、生のポインターを処理することなく、コピーを避けることができます。

最新のコードでstd::string_viewは、const std::string&関数パラメーターのほぼすべての使用を置き換える必要があります。std::string変換演算子をに宣言しているため、これはソース互換の変更である必要がありますstd::string_view

とにかく文字列を作成する必要がある特定のユースケースでは文字列ビューが役に立たないからといって、それが一般的に悪い考えであることを意味するわけではありません。C ++標準ライブラリは、利便性よりも一般性のために最適化される傾向があります。文字列ビューを自分で作成する必要はないため、「安全性が低い」引数は保持されません。


2
の大きな欠点はstd::string_viewc_str()メソッドが存在しないこと std::stringです。そのため、構築および割り当てが必要な不必要な中間オブジェクトが生じます。これは特に低レベルAPIの問題です。
マティアス

1
@Matthiasこれは良い点ですが、大きな欠点とは思いません。文字列ビューを使用すると、あるオフセットで既存の文字列を指すことができます。その部分文字列をゼロで終了することはできません。そのためにはコピーが必要です。文字列ビューでは、コピーの作成が禁止されていません。イテレータで実行できる多くの文字列処理タスクを許可します。しかし、C文字列を必要とするAPIはビューから利益を得ないというのは正しいことです。これにより、文字列参照がより適切になります。
アモン

@ Matthias、string_view :: data()はc_str()と一致しませんか?
アエリアン

3
@Jeevaka C文字列はゼロで終了する必要がありますが、文字列ビューのデータは既存の文字列を指すため、通常はゼロで終了しません。たとえば、文字列abcdef\0cde部分文字列を指す文字列ビューeがあるf場合、元の文字列の後にゼロ文字はありません。標準もノート:「データは、()null終端でないバッファへのポインタを返すことがあります。したがって、それだけのconstチャート*を取り、NULL終端された文字列を期待する関数にデータを()合格するのは間違いでは一般的にある「。
アモン

1
@kayleeFrye_onDeckデータはすでにcharポインターです。C文字列の問題はcharポインターを取得することではありませんが、C文字列はnullで終了する必要があることです。例については、以前のコメントを参照してください。
amon

8

主に最適化の目的で、比較的安全なオブジェクトを安全性の低いオブジェクト(基本的には栄光のポインタと長さ)に普遍的に置き換えることを提唱しているように見えるため、このアドバイスは驚くべきものです。

これはこの目的を少し誤解していると思います。これは「最適化」ですが、実際にはを使用しなくても済むようにする必要がありますstd::string

C ++のユーザーは、多数の異なる文字列クラスを作成しました。固定長文字列クラス、バッファサイズがテンプレートパラメータであるSSO最適化クラス、それらの比較に使用されるハッシュ値を格納する文字列クラスなど。COWベースの文字列を使用する人もいます。C ++プログラマがやりたいことが1つある場合は、文字列クラスを作成します。

また、Cライブラリが作成および所有する文字列は無視されます。char*おそらく、何らかのサイズの裸のs。

したがって、ライブラリを作成していて、を取得するconst std::string&場合、ユーザーは使用している文字列をすべて取得し、にコピーする必要がありstd::stringます。たぶん何十回も。

std::stringの文字列固有のインターフェイスにアクセスする場合、なぜ文字列をコピーする必要があるのですか?それはとても無駄です。

string_viewパラメータとして使用しない主な理由は次のとおりです。

  1. 最終的な目標が、文字列をNULで終了する文字列(fopenなど)を受け取るインターフェイスに渡すことである場合。std::stringNUL終了が保証されています。string_viewそうではありません。また、ビューをサブストリング化して、NULで終了しないようにすることは非常に簡単です。サブストリングa std::stringは、サブストリングをNUL終了範囲にコピーします。

    まさにこのシナリオのために、特別なNULで終了するstring_viewスタイルタイプを作成しました。ほとんどの操作を実行できますが、NUL終了ステータスを壊す操作はできません(たとえば、最後からトリミングする)。

  2. 生涯の問題。本当にstd::stringそれをコピーする必要がある場合、または文字列を関数呼び出しよりも長くする必要がある場合は、const std::string &。または、単なるstd::string値パラメーターとして。そうすれば、そのような文字列が既にある場合、すぐに所有権を主張でき、呼び出し元は文字列のコピーを保持する必要がない場合、文字列に移動できます。


これは本当ですか?これ以前にC ++で知っていた唯一の標準文字列クラスはstd :: stringでした。Cとの後方互換性のためにchar *を「文字列」として使用するためのサポートがいくつかありますが、私はそれを使用する必要はほとんどありません。確かに、想像できるほとんどすべてのユーザー定義のサードパーティクラスがあり、文字列はおそらくそこに含まれていますが、私はそれらを使用する必要はほとんどありません。
TED

@TED:「ほとんど使用する必要がない」からといって、他の人が日常的にそれらを使用しないという意味ではありません。string_view何でも動作する共通語タイプです。
ニコルボーラス

3
@TED:それが、「言語/ライブラリとしてのC ++」ではなく、「プログラミング環境としてのC ++」と言った理由です。
ニコルボーラス

2
@TED:「それで、「プログラミング環境としてのC ++には何千ものコンテナクラスがある」と同じように言えますか?」しかし、イテレーターで動作するアルゴリズムを作成することができ、そのパラダイムに従うすべてのコンテナークラスがイテレーターで動作します。対照的に、文字の任意の連続した配列を取ることができる「アルゴリズム」は書くのがはるかに困難でした。でstring_view、それは簡単です。
ニコルボーラス

1
@TED:文字配列は非常に特殊なケースです。それらは非常に一般的であり、連続する文字の異なるコンテナはメモリの管理方法のみが異なり、データ全体の反復方法は異なります。そのため、テンプレートを使用することなく、このようなすべてのケースをカバーできる単一の共通語範囲タイプを使用することは理にかなっています。これを超える一般化は、範囲TSとテンプレートの領域です。
ニコルボーラス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.