Should we prefer temporary variables over user defined variables in c++

Lets say there is a c++ function foo() which returns a boolean.

I call this functions to check the status of a property Or to get the result of the function call.

So what would be the best way to call this type of functions.

Method 1 :

bool flag = foo()
if (flag)
{
   // some code
}
else
{
   // else some code
}

Method 2:

if ( foo() )
{
   // some code
}
else
{
   // some code
}

My Question: Does using the temporary variable gives compiler opportunity to better optimize in general.

First of all, let us remember than a compiler does not have the same view of the source code as we do: syntax does not matter to it, only semantics. As such, we could argue it has a rather symbolic view of the code.

Furthermore, when the optimizer comes into to play, it will apply analyses and transformations that will turn equivalent symbolic executions into the equivalent binary.

Therefore, what matters for optimization is semantics. There are 3 different syntactic forms that can be used for an if statement:

// with a temporary
if (make()) { ... } else { ... }

// with a variable already in scope
auto var = make();
if (var) { ... } else { ... }

// with a new variable
if (auto var = make()) { ... } else { ... }

The latter syntax is desugared to:

{ auto var = make(); if (var) { ... } else { ... } }

There is a semantic difference: the scope of those variables differ.

  • in the temporary case, its lifetime ends before the if or else block is executed
  • in the named variable case, its lifetime ends at the end of its scope, after the else thus

In general, the difference of lifetime should be negligible, semantically. There are some cases though where it might matter: if you need to access it or if the side effects need be properly sequenced.

There might be some effects on the emitted code:

  • for a built-in type: it is unlikely there will be any effect
  • for a POD type: it might not be optimized as clearly
  • for a non-POD type: the side-effects in the destructor (such as releasing memory) may inhibit optimizations

And which optimizations ?

Well, the primary optimization that could be applied here is stack reuse, whenever an object's lifetime has ended the compiler should be free to reuse its memory space for another object (see -fstack-reuse).

If the compiler cannot prove the object is no longer used, or must keep it alive because of side-effects in its destructors, then it might have to occupy more space on the stack for this object. As mentioned, it's unlikely this will occur (with optimizations turned on) when it comes to built-in types, but with a std::string unfortunately I would not be surprised.

let's disregard methods and look only at const objects; the compiler has much more opportunity for optimization here. if an object is declared const, then (iso/iec 14882:2003 7.1.5.1(4)):

except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

lets disregard objects that may have mutable members - the compiler is free to assume that the object will not be modified, therefore it can produce significant optimizations. these optimizations can include things like:

  • incorporating the object's value directly into the machines instruction opcodes
  • complete elimination of code that can never be reached because the const object is used in a conditional expression that is known at compile time
  • loop unrolling if the const object is controlling the number of iterations of a loop

note that this stuff applies only if the actual object is const - it does not apply to objects that are accessed through const pointers or references because those access paths can lead to objects that are not const (it's even well-defined to change objects though const pointers/references as long as the actual object is non-const and you cast away the constness of the access path to the object).

in practice, i don't think there are compilers out there that perform any significant optimizations for all kinds of const objects. but for objects that are primitive types (ints, chars, etc.) i think that compilers can be quite aggressive in optimizing the use of those items.

  1. std::string manages its own memory, so you can copy, create, destroy them easily.
  2. you can't use your own buffer as a std::string.
  3. you need to pass a c string / buffer to something that expects to take ownership of the buffer - such as a 3rd party c library.

not in msvc, unfortunately, according to their developer center.

it's very frustrating because we'd like to use it in a couple of cases where the equivalent gcc intrinsic has saved us a critical few microseconds in inner loops, but the closest we can get is to swap the if and else clauses so that the more likely case is in the forward-jump-not-taken branch.

the language provides no way of adding attributes.

of course, if you are using clang, you can edit the source of clang, and add any attributes you like.


Tags: C++ Optimization Compiler Construction