Raw loops

From Sean Parent’s C++ Seasoning talk, he explains how raw loops suffer from several issues:

  • Difficult to reason about and difficult to prove post conditions
  • Error prone and likely to fail under non-obvious conditions
  • Introduce non-obvious performance problems
  • Complicates reasoning about the surrounding code

Instead algorithms should be preferred, either using the algorithms in the standard library or writing a new algorithm. Even more so, Sean Parent suggests that all coding guildines should be replaceed with “No raw loops” in order to improve code quality.

cppcheck

Unless you have someone like Sean Parent on your team, then most likely a lot of raw loops can sneak in when a standard algorithm could’ve been used. In version 1.85, cppcheck can find common loop patterns and suggest standard algorithms.

It can find the simple std::fill:

void f(std::vector<int> v)
{
    for(int& x:v)
        x = 1;
}

Which it will suggest std::fill:

std_fill.cpp:4:11: warning: Consider using std::fill algorithm instead of a raw loop. [useStlAlgorithm]
        x = 1;
          ^

It can find the common any_of pattern, which returns true if any of the elements match the predicate:

bool has_one(std::vector<int> v)
{
    for(int x:v)
        if(x == 1) 
            return true;
    return false;
}

Which warns:

std_find.cpp:4:0: warning: Consider using std::any_of algorithm instead of a raw loop. [useStlAlgorithm]
        if(x == 1)
             ^

It even works when written differently such as setting a boolean and stopping:

bool has_one(std::vector<int> v)
{
    for(auto x:v) 
    {   
        if(x == 1) 
        {
            b = true;
            break;
        }
    }
    return b;
}

Which warns:

std_find.cpp:4:9: warning: Consider using std::any_of algorithm instead of a raw loop. [useStlAlgorithm]
        {
        ^

There are many more patterns it can recognize and hopefully more can be added in the future.

Index-based loops

Of course this currently only works with range-based for loops, unfortunately. It takes more work to figure out if x[1] has iterators available. If you don’t care about false positives, there are these set of cppcheck rules which can find some common patterns(read more about cppcheck rules here). It can find uses of std::copy which cannot be written as a range-based for loop(at least not without zip iterators):

void f1(int * a, int * b, int n)
{
    for(int i = 0;i < n;i++)
        a[i] = b[i];
}

Which warns:

copy.cpp:3:0: warning: Considering using std::copy instead. [useStlAlgorithm]
    for(int i = 0;i < n;i++)
    ^

Conclusion

Of course, there can be more patterns to recognize in the future. It doesn’t recognize a std::rotate as was discussed in Sean Parent’s talk. Is there a common pattern in your codebases that can be replaced with a standard algorithm? Perhaps, consider contributing it, or maybe writing a cppcheck rule.