Recently I’ve been looking for ways to optimize one of our critical services and noticed significant number of constructor invocations. Turned out those constructors were invoked by the below code
The idea is to reuse cached values if they are available and create a new instance otherwise. The problem is that I made sure that we should always have cached values, so no new instances should ever be created. Even though continuous profiling infra didn’t make it clear which constructor was invoked, I figured that it must be a copy constructor. But why would copies be involved - we’re just binding std::optional value to const reference, after all.
To investigate I’ve used a following setup
and looked at the output for
Take a second and try to predict the output before checking the actual output below
132
So the value is indeed copied (2). But why? The easiest way to understand this is to rewrite it using immediately invoked lambda
that produces the exact same output. But now we can add an explicit type
and get a compiler warning:
<source>: In lambda function:<source>:27:25: warning: returning reference to temporary [-Wreturn-local-addr] 27 | return build(); | ~~~~~^~
What this means is that it’s obviously not the return type that compiler used for Elvis operator and instead it used a non-reference return type.
So what can we do instead? In my case, since I know for a fact that cached value must always be present, I was able to get rid of the Elvis operator and simply bind its value to a const ref. But if that wasn’t the case, the simplest, albeit somewhat ugly solution would be to use a plain old if statement
This gets rid of the copy.
You can play with different approaches using compiler explorer.