C++ Tips: Replace i++ with ++i

  • 2020-06-12 10:14:50
  • OfStack

Static code analysis tools simplify coding, detect errors, and help fix them. PVS-Studio is a static code analysis tool for C/C++. The team examined more than 200 C/C++ open source projects, including well-known Unreal Engine, Php, Haiku, Qt and Linux kernels. So they share one mistake a day and give advice.

This bug is found in the source code for Unreal Engine 4.

Error code:


void FSlateNotificationManager::GetWindows(
 TArray< TSharedRef<SWindow> >& OutWindows) const
{
 for( auto Iter(NotificationLists.CreateConstIterator());
    Iter; Iter++ )
 {
  TSharedPtr<SNotificationList> NotificationList = *Iter;
  ....
 }
}

Explanation:

If you don't read the title, you might have a hard time finding the problem in the code. This code looks perfect at first glance, but it's not perfect. That's right, I'm referring to the postscript operator Iter++. We should try to use the preceding and not the following autoincrement operators, namely ++ Iter instead of Iter++. Why would you do that? What's the real value? I'll explain that in more detail.

Correct code:


void FSlateNotificationManager::GetWindows(
 TArray< TSharedRef<SWindow> >& OutWindows) const
{
 for( auto Iter(NotificationLists.CreateConstIterator());
    Iter; ++Iter)
 {
  TSharedPtr<SNotificationList> NotificationList = *Iter;
  ....
 }
}

Advice:

The difference between prefix and suffix forms is well known. I hope the difference in their internal structure is also clear to you. If you've ever used operator overloading, you're probably already aware of it. If not, I'll briefly explain 1 here (those who have used operator overloading can skip the following example on operator overloading).

The former auto-increment operator changes the state of the object and returns the changed state of the object without creating a temporary object. Here is an example of the preceding autoincrement operator:


MyOwnClass& operator++()
{
 ++meOwnField;
 return (*this);
}

The post-increment operator also changes the state of the object but returns the state of the object before the change, and a temporary object needs to be created. Here is an example of post-additive operator overload:


MyOwnClass operator++(int)
{
 MyOWnCLass tmp = *this;
 ++(*this);
 return tmp;
}

If you look at the above code, you will see that there is an extra operation to create a temporary object, which is very important in practice!

Today's compilers are smart enough to optimize code that they don't create temporary objects if they aren't useful. This is why it is difficult to see the difference between i++ and ++i in the release.

But it's a different story when you're debugging your program in debug mode, and you can see a big difference in performance.

There are a few examples to estimate the running time of the code using pre - and post - autoincrement operators in the debug version, and we can see that using the suffix form takes almost four times as long as the prefix.

Some people say, "So what? It's the same release anyway." This idea is both true and false. We usually spend more time doing unit testing and debugging, so we spend most of our time working on debug builds. Nobody wants to waste their time waiting, right?

On "For iterators, should we use the preceding autoincrement operator (++i) instead of the following autoincrement operator (i++)?" I want to seriously answer the question, "Yes, it should be done." You'll find a lot of speed improvements in debug builds. This is especially useful if the iterator is complex.

This error was detected using static code analysis tool ES59en-ES60en with the error message V803 performance degradation. If iter is an iterator, it is more efficient to use the preceding autoincrement operator, using ++iter instead of iter++.

conclusion


Related articles: