メインコンテンツ

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

範囲ベースの for ループの反復における計算量の多いコピー

範囲ベースの for ループのループ変数が参照されるのではなく範囲要素からコピーされるため、非効率なコードになる

説明

この欠陥は、範囲ベースの for ループのループ変数が、範囲要素を参照で読み取るのではなく、範囲要素からコピーされた場合に発生します。範囲要素は必要な場合にのみコピーします。これは、範囲要素をコピーすると非効率なコードになる可能性があるためです。この欠陥は、ループ変数が変更されておらず、以下の条件のいずれかが true の場合に発生します。

  • コピーするループ変数が大きなトリビアル コピー可能な型変数である。オブジェクトが大きい場合は、トリビアル コピー可能なオブジェクトのコピーの方が参照より計算量が多くなります。

  • コピーされるループ変数が非トリビアル コピー可能な型である。このようなオブジェクトをコピーするには、参照よりも計算量の多い外部関数呼び出しが必要になります。オブジェクトが非トリビアル コピー可能かどうかをチェックするには、関数 std::is_trivially_copyable を使用します。この関数の詳細については、C++ リファレンスの std::is_trivially_copyable を参照してください。

リスク

範囲ベースの for ループは、ループの反復ごとに時間のかかるループ変数のコピーが実行された場合に非効率になる可能性があります。次のコードについて考えます。

void foo( std::map<std::string, std::string> const& property_map )
{
    for( std::pair< const std::string, std::string > const property: property_map) 
    {}
}

ループ変数 property は、const& ではなく const として宣言されています。for ループの反復ごとに、std::pair オブジェクトがマップ property_maps からループ変数 property にコピーされます。propert の宣言から & が欠落しているため、反復ごとに、参照操作ではなく、計算量の多いコピー操作が行われるため、非効率なコードになります。このコードはコンパイル可能で、正しく機能するため、非効率な for ループが検出されない可能性があります。同様の非効率性の原因については、計算量の多い値渡し計算量の多い値返しを参照してください。

修正方法

この欠陥を修正するには、範囲ベースの for ループのループ変数を const& として宣言します。次のコードについて考えます。

void foo( std::map<std::string, std::string> const& property_map )
{
    for( std::pair< const std::string, std::string > const& property: property_map) 
    {}
}
ループ変数 propertyconst& として宣言されているため、この変数は、リソースをコピーせずに、ループ反復ごとにマップ property_map の異なる要素を参照します。各反復での計算量の多いコピーを回避することによって、コードが効率化します。

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

すべて展開する

#include <initializer_list>
#include <unordered_map>
#include <vector>
struct Small_Trivial_Type
{
	unsigned char values[ sizeof( void* ) ];
};

struct Large_Trivial_Type
{
	unsigned char values[ 4u * sizeof( void* ) ];
};

class Nontrivial_Type
{
	Nontrivial_Type() noexcept;
	Nontrivial_Type( Nontrivial_Type const& );
	Nontrivial_Type& operator=( Nontrivial_Type const& );
	~Nontrivial_Type() noexcept;
	int read() const;
	void modify( int );
};
extern std::vector< Nontrivial_Type > getNtts();

void foo( std::vector< Nontrivial_Type > const& ntts )
{
	for( Nontrivial_Type ntt: ntts )
	{}
}

void foo_auto( std::vector< Nontrivial_Type > const& ntts )
{
	for( auto ntt: ntts ) 
	{}
}
void foo_c_array( Nontrivial_Type const ( & ntts )[ 10 ] )
{
	for( Nontrivial_Type ntt: ntts )
	{}
}
void foo_large( std::vector< Large_Trivial_Type > const& ltts )
{
    for( Large_Trivial_Type ltt: ltts ) 
    {}
}
void foo_small( std::vector< Small_Trivial_Type > const& stts )
{
    for( Small_Trivial_Type const stt: stts ) 
    {}
}
void modify_elem( std::vector< Nontrivial_Type > const& ntts )
{
	for( Nontrivial_Type ntt: ntts ) 
	{
		ntt.modify( 42 );//Modification
	}
}

この例では、さまざまな型のループ変数を含む範囲ベースの for ループが示されます。

  • Polyspace® は、ループ変数が変更されていないために、計算量の多い不必要なコピー操作による、foo() 内の非トリビアル コピー可能なループ変数 ntt にフラグを設定します。同じ理由で、foo_auto()foo_c_array() 内のループ変数にもフラグが設定されます。

  • Polyspace は、foo_large() 内の大きなループ変数 ltt にフラグを設定します。これは、ltt がトリビアル コピー可能な型であっても、ltts の要素を参照するより、ltts の要素を ltt にコピーする方が計算量が多くなるためです。

  • Polyspace は、foo_small() 内のループ変数 stt にフラグを設定しません。これは、stts の要素を stt にコピーする方が stts の要素を参照する場合に比べて計算量が増えないためです。

  • Polyspace は、modify_elem() 内のループ変数 ntt にフラグを設定しません。これは、ループ変数がループ内で変更されるためです。

修正

この問題を修正するには、範囲ベースの for ループでループ変数として定数参照 (const&) を使用します。const& ループ変数を使用すれば、計算量の多いコピーが回避され、効率的なコードが生成されます。

#include <initializer_list>
#include <unordered_map>
#include <vector>
struct Small_Trivial_Type
{
	unsigned char values[ sizeof( void* ) ];
};

struct Large_Trivial_Type
{
	unsigned char values[ 4u * sizeof( void* ) ];
};

class Nontrivial_Type
{
	Nontrivial_Type() noexcept;
	Nontrivial_Type( Nontrivial_Type const& );
	Nontrivial_Type& operator=( Nontrivial_Type const& );
	~Nontrivial_Type() noexcept;
	int read() const;
	void modify( int );
};
extern std::vector< Nontrivial_Type > getNtts();
// Test iterating over a const vector.
void foo( std::vector< Nontrivial_Type > const& ntts )
{
	for( Nontrivial_Type const& ntt: ntts ) // NC2C
	{}
}

void foo_auto( std::vector< Nontrivial_Type > const& ntts )
{
	for( auto const& ntt: ntts ) //NC2C
	{}
}
void foo_c_array( Nontrivial_Type const ( & ntts )[ 10 ] )
{
	for( Nontrivial_Type const& ntt: ntts ) // NC2C
	{}
}
void foo_large( std::vector< Large_Trivial_Type > const& ltts )
{
	for( Large_Trivial_Type const& ltt: ltts ) 
	{}
}

結果情報

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

バージョン履歴

R2020b で導入