Return Value Optimization disabled for static variable
TL;DR
If a function returns a static variable, there won’t be any return value optimization (RVO), as static variable is allocated for the lifetime of the program, it can’t be moved.
What are RVO & Copy Elision?
In C++ computer programming, copy elision refers to a compiler optimization technique that eliminates unnecessary copying of objects.
…
Return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function’s return value.from Wikipedia
With the help of RVO and copy elision, it saves the object copy operation (sometimes it could be very expensive on large object) by moving the source object to the new one. More details can be found from Copy Elision and Return Value Optimization in C++.
There are two cases where copy elision could be applied:
- returning a local variable inside a function
- initializing a variable with a temporary value
Static variable
A static variable is allocated only once in a program lifetime in static storage area. They have a scope till the program lifetime.
When a variable is declared as static in a function, space for it gets allocated for the lifetime of the program, not the function. Even if the function is called multiple times, space for the static variable is allocated only once and the value of variable in the previous call gets carried through the next function call.
RVO doesn’t work for static variable
As static variable has the lifetime of the program, it can’t be moved when the function exits, hence RVO won’t be triggered and copy constructor will be called.
Here’s an example, we define a class Point
, explicitly declare its normal constructor and copy construction to tell the difference.
1 | class Point |
Let’s create a program that creates a Point
object and prints it’s content.
1 | Point GetPoint() { |
The function GetPoint()
initializes a local variable p
, returns it directly, the caller takes the return value and initialzes another local variable p
. It triggers copy elision and RVO, the output is as below, no copy operation.
1 | >> Normal constructor called. |
Let’s add a new function GetConstPoint2()
and create a static variable inside.
1 | const Point GetConstPoint2() { |
As the static varialble outlive the scope of the function, it can’t be moved, so RVO can’t be applied here. Here’s the output, the second run has an extra “Copy constructor called”.
1 | >> Normal constructor called. |
Can we avoid the extra copy for static variable?
Yes.
Since it’s a static variable, we don’t have to worry about its lifetime, we can return its pointer to avoid the object copy.
1 | const Point* GetConstPoint3() { |
As using a const pointer, we can not only access the variable without an object copy, but also keep it immutable (safe), here’s the output:
1 | >> Normal constructor called. |
Full example code can be found here: https://onlinegdb.com/rk0fbM3rI.
Sum up
RVO doesn’t work for static variable, which has been allocated in program lifetime and can’t be moved.
Once we understand how and why copy elision and RVO are helpful to code performance optimization, we would know when to use it and when not to.