The C++ Core Guidelines recommend using lambda initialization for complex initialization: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-lambda-init
It gives the following reason:
Reason It nicely encapsulates local initialization, including cleaning up scratch variables needed only for the initialization, without needing to create a needless nonlocal yet nonreusable function. It also works for variables that should be const but only after some initialization work.
These examples use GCC 9.1 on x86-64 using -O2.
The following program with lambda initialization:
#include <ctime>
int func() {
return std::time(nullptr);
}
int main() {
const int x = std::time(nullptr);
const int a = [&]() {
const int e = func() + x * func();
const int f = func() * func();
return e + f;
}();
return a;
}Compiles to the following assembly:
func():
sub rsp, 8
xor edi, edi
call time
add rsp, 8
ret
main:
push r12
xor edi, edi
push rbx
sub rsp, 8
call time
xor edi, edi
mov r12, rax
call time
xor edi, edi
mov rbx, rax
call time
xor edi, edi
imul r12d, eax
call time
xor edi, edi
add r12d, ebx
mov rbx, rax
call time
add rsp, 8
imul ebx, eax
lea eax, [rbx+r12]
pop rbx
pop r12
ret
The following program without lambda initialization:
#include <ctime>
int func() {
return std::time(nullptr);
}
int main() {
const int x = std::time(nullptr);
const int e = func() + x * func();
const int f = func() * func();
const int a = e + f;
return a;
}Compiles to the following assembly:
func():
sub rsp, 8
xor edi, edi
call time
add rsp, 8
ret
main:
push r12
xor edi, edi
push rbp
sub rsp, 8
call time
xor edi, edi
mov rbp, rax
call time
xor edi, edi
mov r12, rax
call time
xor edi, edi
imul ebp, eax
call time
xor edi, edi
add r12d, ebp
mov rbp, rax
call time
add rsp, 8
imul eax, ebp
pop rbp
add eax, r12d
pop r12
ret