This rule is part of MISRA C++:2023.
Usage of this content is governed by Sonar’s terms and conditions. Redistribution is
prohibited.
Rule 9.5.1 - Legacy for statements should be simple
[stmt.for]
Category: Advisory
Analysis: Decidable,Single Translation Unit
Amplification
A legacy for statement is simple when:
- The init-statement only declares and initializes a loop-counter of integer type; and
- The condition only compares the loop-counter to a loop-bound using a relational operator; and
- The loop-counter is modified, but only by incrementing or decrementing by a loop-step within the expression of the
for statement; and
- The loop-bound and loop-counter have the same type, or the loop-bound is a constant expression and the type of the
loop-counter has a range large enough to represent the value of the loop-bound; and
- The loop-bound and loop-step are constant-expressions or are variables that are not modified within the for
statement; and
- The loop-counter, loop-bound and loop-step are not bound to non-const references and do not have any of
their addresses assigned to pointers to non-const.
Note: the range-for statement is not a legacy for statement.
Rationale
The number of iterations of a legacy for statement is determined by a user-provided loop condition and code review, which may be
non-trivial, is required to ensure that the loop behaves as expected. This review is not required for iterator-based algorithms or range-for
statements, as the number of iterations is not determined by a user-provided loop condition. It is therefore recommended that legacy for
statements should not be used, unless they are simple.
It is generally unnecessary to use the legacy for statements as C++ Standard Library algorithms are provided for most iteration use-cases.
Iterating over the contents of a container can be achieved by the use of a range-for statement when the existing algorithms are not suitable.
Using or implementing a range adapter or iterator adapters allows range-for statements or iterator-based algorithms to be used to loop over
other data sources and sinks.
When a legacy for statement cannot be replaced by an existing C++ Standard Library algorithm, it can be abstracted and confined within a
(potentially generic) dedicated function to make code review and justification easier.
Note: care must be taken to ensure that a simple legacy for statement will make progress and terminate.
Example
for ( int32_t i = 0; i < 10; ++i ) // Compliant
{
cout << i << " ";
}
bool foo( int32_t & );
for ( int32_t i = 0; i < 10; ++i ) // Non-compliant
{
foo( i ); // i passed as non const & parameter
}
for ( uint32_t i = 0u; i < u64a; ++i ) // Non-compliant - loop-counter and
{ // loop-bound have different types
// ...
}
int32_t sum { };
std::array< int32_t, 10 > arr { };
for ( auto i = 0u; i < arr.size(); ++i ) // Compliant- arr.size() is constant
{
sum += arr[ i ];
}
The following achieve the same without the use of legacy for statements:
for ( auto const e : arr ) // Rule does not apply
{
sum += e;
}
sum = reduce( begin( arr ),
end( arr ),
int32_t {} ); // Rule does not apply
Copyright The MISRA Consortium Limited © 2023