Main Content

MISRA C++:2023 Rule 28.3.1

Predicates shall not have persistent side effects

Since R2024b

Description

Rule Definition

Predicates shall not have persistent side effects.

Rationale

The MISRA™ C++:2023 standard defines these actions as persistent side effects:

  • Modifying a file or a stream.

  • Modifying an object, either directly or through a pointer or reference.

  • Accessing a volatile object.

  • Raising an exception that is not handled in the current function.

A predicate is a callable entity that binds to the template arguments called Compare, Predicate, or BinaryPredicate in the Standard Template Library (STL). The C++ standard does not prohibit copying predicates when templates are invoked. If your C++ implementation copies predicates, persistent side effects can occur and can result in unexpected behavior. The C++ standard also allows algorithms to invoke predicates in any order and on any object. If your predicates have persistent side effects that rely on the predicates being invoked in a specific order or on specific objects, your code can have unexpected behavior that is difficult to resolve.

To avoid unexpected and unpredictable behavior, the MISRA C++:2023 standard mandates that predicates not have any persistent side-effect. In addition, if a predicate is a function object, its operator() must also be const.

Polyspace Implementation

For this rule, Polyspace® checks predicates that are accepted by templates from the (STL) and predicates that are passed to std::function objects. A violation is reported if a any of these conditions are true:

  • A predicate lambda captures an object using a non-const reference.

  • A predicate lambda is specified as mutable.

  • The operator() of a predicate class or struct is not qualified as const.

  • A predicate raises an exception using the throw statement.

  • A predicate function is not a pure function. For details about how Polyspace determines the purity of a function, see MISRA C:2012 Rule 13.5.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

In this example, the lambda predicate of the STL function std::find_if() captures the variable flag by non-const reference when calculating the iterator iterator. Because the variable is captured as a non-const reference, the predicate is able to modify this variable as a persistent side effect. Polyspace reports a violation on the lambda. When calculating iterator2, the lambda predicate captures the object as const, which is compliant with this rule.

#include <vector>
#include <algorithm>

void foo() {
	std::vector<int> numbers = {2, 4, 6, 8, 9, 10};
	bool flag;

	// Use std::find_if with a lambda predicate to find the first odd number
	auto iterator = std::find_if(numbers.begin(), numbers.end(), [&flag](int n) { //Noncompliant

		//...
	});
	//...
	auto iterator2 = std::find_if(numbers.begin(), numbers.end(), [const &flag](int n) { //Compliant

		//...
	});
	//...

}

In this example, the operator() of the predicate Comparator is not const qualified. This predicate is able to modify the input parameter as a persistent side effect. Polyspace reports a violation on the operator() definition. The operator() of Comparator2 is const-qualified, which is compliant.

#include <set>

struct Comparator
{
   bool operator()( int32_t x, int32_t y ) // Noncompliant
   {
     //...
   }
};
struct Comparator2
{
   bool operator()( int32_t x, int32_t y ) const // Compliant
   {
     //...
   }
};

std::set< int32_t, Comparator > a_set;
std::set< int32_t, Comparator2 > b_set;

Check Information

Group: Algorithms library
Category: Required

Version History

Introduced in R2024b