如何在不调用析构函数的情况下将值移出 std:optional ?

use*_*702 5 c++ destructor move-semantics c++17 stdoptional

我正在尝试编写一个函数,make_foo,它将“解开” a std::optional< foo >,返回包含的值。该函数假定可选的已启用,因此不会对optional.

我的实现如下,以及编译后的程序集以供参考。我有几个关于编译器输出的问题:

  1. 为什么这会导致代码分支?optional::operator*提供对包含值的未经检查的访问,所以我不希望看到任何分支。

  2. 为什么foo会调用析构函数?注意on_destroy()程序集中的调用。我们如何在不调用析构函数的情况下将包含的值移出可选项?

Godbolt 链接

C++17 源代码

#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();
}

Run Code Online (Sandbox Code Playgroud)

优化的编译器输出 (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
Run Code Online (Sandbox Code Playgroud)

eer*_*ika 3

如何在不调用析构函数的情况下将值移出 std:Optional ?

就像您在示例中所做的那样。移动不会调用析构函数。的析构函数foo由 的析构函数调用,std::optional而 的析构函数由您创建的临时对象的析构函数调用std::optional

您只能通过泄漏对象或从一开始就避免对象的创建(以及移动)来防止对象被破坏。

为什么这会导致分支代码?

的析构函数中有一个分支std::optional。仅当 不为空时,才会调用所包含对象的析构函数std::optional

optional::operator*提供对所包含值的未经检查的访问,因此我不希望看到任何分支。

理论上,如果优化器足够聪明,它可能会使用这些知识无条件地调用析构函数,因为它可能知道如果函数返回空,则程序的行为是未定义的std::optional。它似乎还不够聪明,无法做出这样的优化。