I am trying to write a function, make_foo
, that will "unwrap" a std::optional< foo >
, returning the contained value.
The function assumes that the optional is engaged so does not perform any runtime checks on the optional
.
My implementation of this is below, along with the compiled assembly for reference. I have a couple of questions about the compiler output:
Why does this result in branching code?
optional::operator*
gives unchecked access to the contained value, so I would not expect to see any branching.Why does
foo
's destructor get called? Note the call toon_destroy()
in the assembly. How do we move the contained value out of the optional without calling the destructor?
C++17 source
#include <optional>
extern void on_destroy();
class foo {
public:
~foo() { on_destroy(); }
};
extern std::optional< foo > foo_factory();
// Pre-condition: Call to foo_factory() will not return nullopt
foo make_foo() {
return *foo_factory();
}
Optimized compiler output (Clang 11)
make_foo(): # @make_foo()
push rbx
sub rsp, 16
mov rbx, rdi
lea rdi, [rsp + 8]
call foo_factory()
cmp byte ptr [rsp + 9], 0
je .LBB0_2
mov byte ptr [rsp + 9], 0
call on_destroy()
.LBB0_2:
mov rax, rbx
add rsp, 16
pop rbx
ret