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