メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

より効率的なオーバーロードの代わりに、計算量の多い std::string メソッドを使用

一重引用符で囲んだ文字の代わりに、長さが既知の文字列リテラルで std::string メソッドが呼び出される

R2021a 以降

説明

この欠陥は、一重引用符で囲んだ文字の代わりに、長さが既知の文字列リテラルを使用して特定の std::string メソッドを呼び出す場合に発生します。文字列リテラルを使用して特定の std::string メソッドを呼び出すと、リテラルの長さがコンパイル時に既知であっても、メソッドがその長さを計算しなければなりません。Polyspace はこのような呼び出しに対し、非効率としてフラグを設定します。たとえば、Polyspace® は次の std::string::find の呼び出しのうち、最初の 2 つの呼び出しにフラグを設定します。

std::string str;
//...
str.find("A");//Inefficient
//...
str.find("ABC",offset,1);//Inefficient
str.find('A');//Efficient
最初の 2 つの呼び出しでは、文字列リテラル "A" の長さが実行前に既知であるのに、コンパイラはその長さを計算しています。3 番目の呼び出しでは、コンパイラは入力の長さを計算しないため、この呼び出しは最初の 2 つよりも効率的です。一重引用符で囲んだ文字の代わりに、長さが既知の文字列リテラルを使用して以下の std::string メソッドが呼び出される場合、Polyspace はこの欠陥を報告します。

  • find

  • rfind

  • find_first_of

  • find_last_of

  • find_first_not_of

  • find_last_not_of

  • replace

  • operator=

  • operator+=

  • starts_with (C++20)

  • ends_with (C++20)

リスク

場合によっては、文字列リテラルまたは一重引用符で囲んだ文字のいずれかを使用して std::string メソッドを呼び出すことができます。その場合、文字列リテラルを使用した std::string メソッドの呼び出しは、実行前に既知となっている文字列リテラルの長さをコンパイラに計算させるため、非効率です。std::string メソッドは頻繁に使用されることから、このような非効率なメソッド呼び出しによって、コードの計算量が増え、非効率となる可能性があります。

修正方法

この問題を修正するには、適切な場合は文字列リテラルではなく、一重引用符で囲んだ文字を使用して std::string メソッドを呼び出します。たとえば、単一の文字や単一の文字の繰り返しからなる文字列リテラルの代わりに、一重引用符で囲んだ文字を入力として使用できる場合があります。一重引用符で囲まれた文字を受け入れる、メソッドの別のオーバーロードを使用しなければならない場合もあります。

パフォーマンスの改善の程度は、使用しているコンパイラ、ライブラリ実装、環境によって異なる可能性があります。

すべて展開する

#include <string>
#include <set>
constexpr size_t ONE(){ return 1; }
void foo(std::string& str){
	int pos, count;
	str += "A";
	str += "\0";
	str.find("AA", 0, ONE());
	str.find("A");
	str.find_first_of("A");
	str.find_last_of("A"); 
	str.find_first_not_of("A");
	str.find_last_not_of("A");
	str.replace(0, 1, "A");
	str.rfind("AA", 0,1);
	str.replace(0, count, "AAAAA");
	str.replace(pos, count, "AB", 1);
}

この例では、文字列操作を行うために std::string クラスの複数のメソッドと演算子が呼び出されます。これらの呼び出しでは、プログラムの実行前に、文字列リテラルの長さが既知となっています。入力が string オブジェクトとして渡されることから、コンパイラは線形検索を行ってオブジェクトの長さを検出しますが、これでは冗長となります。Polyspace は、このように非効率な文字列操作の呼び出しにフラグを設定します。

修正 – char を入力として受け入れるオーバーロードを使用

この問題を修正するには、文字列リテラルではなく一重引用符で囲んだ文字を使用します。replace などのメソッドの場合、関数呼び出しを書き換えて、入力が単一文字の場合に char 型の変数を受け入れるオーバーロードを使用します。

#include <string>
#include <set>
constexpr size_t ONE(){ return 1; }
void foo(std::string& str){
	int pos, count;
	str += 'A';
	str += '\0';
	str.find('A');
	str.find_first_of('A');
	str.find_last_of('A'); 
	str.find_first_not_of('A');
	str.find_last_not_of('A');
	str.replace(0, 1, 1,'A');//Rewritten call that accepts a single char
	str.rfind('A');
	str.replace(0, count, 5,'A');//Rewritten call that accepts a single char
	str.replace(pos, count, 1,'A');
}

結果情報

グループ: パフォーマンス
言語: C++
既定値: オフ
コマンド ライン構文: EXPENSIVE_USE_OF_STD_STRING_METHODS
影響度: Low

バージョン履歴

R2021a で導入