Expensive copy in a range-based for loop iteration

Переменная цикла основанного на области значений for цикл копируется с элементов области значений вместо того, чтобы быть сосланным, приводя к неэффективному коду

Описание

Этот дефект происходит когда переменная цикла основанного на области значений for цикл копируется с элементов области значений вместо того, чтобы читать элементы области значений ссылкой. Скопируйте элементы области значений только, когда его необходимое, потому что копирование их может привести к неэффективному коду. Этот дефект повышен, когда переменная цикла не модифицирована, и любое из этих условий верно:

  • Скопированная переменная цикла является большой тривиально copyable переменной типа. Копирование тривиально copyable объекта является более дорогим, чем ссылка на него, когда объект является большим.

  • Скопированная переменная цикла является нетривиально copyable типом. Копирование такой объектной силы требует внешнего вызова функции, который является более дорогим, чем ссылка на него. Чтобы проверять, copyable ли объект нетривиально, используйте функциональный std::is_trivially_copyable. Для получения дополнительной информации об этой функции, смотрите std::is_trivially_copyable в ссылке C++.

Риск

Основанный на области значений 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 циклы не могут быть замечены. Для подобного источника неэффективности смотрите Expensive pass by value и Expensive return by value.

Фиксация

Чтобы зафиксировать этот дефект, объявите переменную цикла основанного на области значений 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) 
    {}
}
Поскольку переменная цикла property объявляется как const&, ссылки на переменную различный элемент карты 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® отмечает нетривиально copyable переменную цикла ntt в foo() из-за дорогой операции копии, которая является ненужной, потому что переменная цикла не изменяется. По той же причине, переменным цикла в foo_auto() и foo_c_array() отмечаются.

  • Polyspace отмечает большую переменную цикла ltt в foo_large() потому что более дорого скопировать элементы ltts в ltt чем к опорным элементам ltts, даже при том, что ltt тривиально copyable тип.

  • Polyspace не отмечает переменную цикла stt в foo_small() потому что копирование элементов stts в stt не является более дорогим, чем ссылка на элементы stts.

  • Polyspace не отмечает переменную цикла ntt в modify_elem() потому что переменная цикла изменяется в цикле.

Коррекция

Чтобы устранить эту проблему, используйте постоянные ссылки (const&) как переменные цикла в основанном на области значений for циклы. Используя 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++
Значение по умолчанию: Off
Синтаксис командной строки: EXPENSIVE_RANGE_BASED_FOR_LOOP_ITERATION
Удар: носитель
Введенный в R2020b