我正在尝试编写一个函数make_foo
,它将“解包装”std::optional
,返回包含的值。该函数假设optional已被使用,因此不对optional
执行任何运行时检查。
下面是我对此的实现,以及编译后的程序集以供参考。关于编译器输出,我有几个问题:
>
为什么这会导致分支代码?optional::operator*
允许对包含的值进行未经检查的访问,因此我不希望看到任何分支。
为什么调用foo
的析构函数?请注意程序集中对on_destroy()
的调用。如何在不调用析构函数的情况下将包含的值移出可选值?
Godbolt连杆
#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();
}
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
你的方法或多或少是这样的:
foo make_foo() {
auto x = foo_factory();
return *x;
}
其中x
是从工厂返回的option
(在代码中未命名为临时)。当x
被销毁时,它要么调用所包含对象的析构函数(当有一个时)。或者它不破坏包含的对象(当没有对象时)。简而言之:您移出的foo
仍然需要销毁,即使您知道可选内容包含它,编译器也不能销毁,因此产生了分支。
如何在不调用析构函数的情况下将值移出std:optional?
你不能。即使是一个移动的物体,最终也需要被销毁。
如何在不调用析构函数的情况下将值移出std:optional?
就像你在例子中所做的那样。移动不调用析构函数。foo
的析构函数由std::optional
的析构函数调用,该析构函数通过销毁您创建的临时std::optional
对象调用。
您只能通过泄漏对象,或者首先避免对象的创建(从而也避免对象的移动)来防止对象被破坏。
为什么这会导致分支代码?
std::optional
的析构函数中有一个分支。仅当std::optional
不为空时,才调用包含对象的析构函数。
optional::operator*
允许对包含的值进行未经检查的访问,因此我不希望看到任何分支。
理论上,如果优化器足够聪明,它可能会使用这些知识无条件调用析构函数,因为如果函数返回空的std::optional
,它可能知道程序的行为是未定义的。做这样的优化似乎不够聪明。