Expensive logical operation

Логическая операция требует оценки обоих операндов из-за их порядка, что приводит к неэффективному коду

Описание

Этот дефект возникает, когда все эти условия верны:

  • Левый и правый операнды не имеют побочных эффектов.

  • Правый операнд не содержит никаких вызовов для const представители.

  • Левый операнд содержит один или несколько вызовов для const представители.

При оценке возможных побочных эффектов операнда:

  • Polyspace® принимает, что const Представители функции класса не имеют побочных эффектов. Функции, не являющиеся членами, приняты с побочными эффектами.

  • Polyspace обрабатывает операции с плавающей точкой в соответствии со стандартом C++. В C++ 03 или более ранних версиях операции с плавающей точкой не имеют побочных эффектов. В C + 11 или более поздней версии операции с плавающей точкой могут иметь побочные эффекты, такие как изменение флагов состояния с плавающей точкой для указания ненормальных результатов или вспомогательной информации. См. «окружение с плавающей точкой».

  • Polyspace обрабатывает bool оператор преобразования и логический NOT операторы struct или класс как встроенные операторы. Эти операции не рассматриваются как представитель вызовы функций. Стандартная библиотека шаблонов содержит много классов, которые определяют такое bool оператор преобразования или логический NOT оператор.

Риск

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

if(Object.attribute()|| var1){
//...
}
В логическом выражении внутри if оператор, компилятор всегда оценивает вызов функции Object.attribute(). Оценка функции не всегда необходима. Для образца, если var1 вычисляет, чтобы true, тогда логическое выражение всегда вычисляется как true. Потому что var1 является правым операндом, а не левым операндом, компилятор излишне оценивает вызов функции, что неэффективно. Поскольку неэффективный код компилируется и ведет себя правильно, этот дефект может остаться незамеченным.

Зафиксировать

Чтобы исправить этот дефект, разверните порядок операндов в логическом выражении, если левый операнд не выполняет операцию, которая должна быть выполнена перед правым операндом, чтобы безопасно и правильно вычислить правый операнд.

Если это условие не соответствует true, то код опирается на точный порядок, в котором компилятор оценивает отмеченное логическое выражение. Лучшая практика - не полагаться на порядок оценки выражения. Рассмотрите рефакторинг своего кода, чтобы порядок оценки не оказал влияния. Если рефакторинг кода невозможен, разъясните дефект с помощью аннотации или просмотрите информацию. Смотрите Адрес Результаты Polyspace через исправления ошибок или обоснования.

Эффективность улучшения могут варьироваться в зависимости от используемого компилятора, реализации библиотеки и окружения.

Примеры

расширить все

#include <string>
bool updateNewProperty( const std::string& name );
void updateNewMetaProperty( const std::string& name );
volatile char externalFlag;

void updateProperty( const std::string& name )
{
	bool is_new_property = updateNewProperty( name );

	if( name.compare( "meta" ) == 0 && is_new_property ) //Noncompliant
	{
		updateNewMetaProperty( name );
	}
	if( name.compare( "meta" ) == 0 && externalFlag ) //Compliant
	{
		updateNewMetaProperty( name );
	}
	
}

В первом if оператор, переменная is_new_property - правый операнд логического операнда. Функция представителя std::string::compare вызывается как левый аргумент. Компилятор оценивает вызов функции независимо от значения is_new_property, что неэффективно. Polyspace помечает логическую операцию.

Во втором if оператор, volatile переменная externalFlag - правильный операнд. Потому что переменная volatilePolyspace предполагает, что это может вызвать побочный эффект. Потому что volatile переменная может иметь побочный эффект, Polyspace не помечает логическую операцию.

Коррекция

Определите, нужно ли поддерживать порядок операндов, чтобы безопасно и правильно оценить выражение. В этом случае эти два операнда is_new_property и name.compare( "meta" ) == 0 являются независимыми, и изменение их порядка не изменяет значение логического выражения. Чтобы исправить этот дефект, используйте is_new_property как левый операнд.

#include <string>
bool updateNewProperty( const std::string& name );
void updateNewMetaProperty( const std::string& name );
volatile char externalFlag;

void updateProperty( const std::string& name )
{
	bool is_new_property = updateNewProperty( name );
	if(is_new_property && name.compare( "meta" ) == 0 ) //Compliant
	{
		updateNewMetaProperty( name );
	}
	if( name.compare( "meta" ) == 0 && externalFlag ) //Compliant
	{
		updateNewMetaProperty( name );
	}
}

Компилятор оценивает вызов на std::string::compare в первом if оператор только при is_new_property является true.

Когда вы используете операции с плавающей точкой в логическом выражении, Polyspace оценивает побочные эффекты операндов по-разному на основе версии C++, которую вы используете. В C++ 03 или более ранних версиях операции с плавающей точкой не имеют никаких побочных эффектов сами по себе. В C++ 11 или более поздней версии сами операции с плавающей точкой могут иметь побочные эффекты.

class A{
	//...
	float makeFloat() const{
		//..
	}
	void testfloat(){
		if( makeFloat() == 0.1f && fp==0.2f) //Noncompliant
		{
			//...
		}
	}
	
private:
	float fp;		
};
В этом коде, если вы используете C++ 03, ни один из операндов не имеет побочных эффектов. Поскольку левый операнд вызывает представителя вызов функции, Polyspace помечает выражение.

Если вы используете C++ 11 или более позднюю версию, операции с плавающей точкой могут иметь побочные эффекты. В этом случае Polyspace не помечает логическое выражение.

Коррекция

Определите, нужно ли поддерживать порядок операндов, чтобы безопасно и правильно оценить выражение. В этом случае эти два операнда fp==0.2f и makeFloat() == 0.1f являются независимыми, и изменение их порядка не изменяет значение логического выражения. Чтобы исправить этот дефект, используйте fp==0.2f как левый операнд.

class A{
	//...
	float makeFloat() const{
		//..
	}
	void testfloat(){
		if( fp==0.2f && makeFloat() == 0.1f) //Compliant
		{
			//...
		}
	}
	
private:
	float fp;		
};
Компилятор оценивает вызов на makeFloat() только когда fp==0.2f вычисляет, чтобы true.

#include<cstdlib>
class A{
	//...
	bool isLoaded() const { return p != NULL; }
	int get() {
		if(isLoaded() && *p > 0) { // Noncompliant
			return *p;
		}
	}

	
private:
	int* p;		
};

В выражении (isLoaded() && *p > 0), размежевание *p в правом аргументе безопасно только, когда левый аргумент true. Polyspace не проверяет, когда логическое выражение требует такого определенного порядка оценки. Поскольку ни один из операндов не имеет побочных эффектов, и вызов функции представителя является левым операндом, Polyspace помечает операцию.

Коррекция

В этом случае порядок, если операнды в логическом выражении должны быть сохранены, чтобы вычислить выражение безопасно и правильно. Чтобы исправить этот дефект, рефаксируйте свой код. Лучшая практика - не полагаться на порядок оценки выражения.

#include<cstdlib>
class A{
	//...
	bool isLoaded() const { return p != NULL; }
	int get() {
		if(isLoaded()== true) { // Compliant
			if(*p > 0){
				return *p;	
			}
			
		}
	}

	
private:
	int* p;		
};
Этот код проверяет два условия отдельно и не полагается на порядок оценки. Если такой рефакторинг невозможен, обосновайте дефект с помощью аннотаций или просмотрите информацию.

Информация о результатах

Группа: Эффективность
Язык: C++
По умолчанию: Off
Синтаксис командной строки : EXPENSIVE_LOGICAL_OPERATION
Влияние: Средний
Введенный в R2021a