c++ 我可以将一个不可移动和不可复制的函数结果复制到一个可选的结果中吗?

bwleehnv  于 2023-01-15  发布在  其他
关注(0)|答案(3)|浏览(149)

我想在std::optional中存储一个不可移动且不可复制的 non-trivial 类型。但是该对象是由自由函数构造的。(example

struct Foo {
    Foo();
    Foo(Foo const&) = delete;
    Foo(Foo&&) = delete;
    Foo& operator=(Foo const&) = delete; // added for completeness
    Foo& operator=(Foo&&) = delete; // added for completeness
    ~Foo();
};
Foo foo();

不改变Foofoo();
多亏了复制省略,我已经可以做到这一点:

Foo f1 = foo();

这也可以编译,因为std::optional只要求存储类型是可析构的:

std::optional<Foo> f2;
f2.emplace();

但是我不能用函数结果填充f2

f2 = foo(); // no
f2.emplace(foo()); // no

显然,因为这需要拷贝或移动Foo这可能是不可能的,但我是否忽略了什么?

wh6knrhe

wh6knrhe1#

你可以创建一个类,其中转换操作符调用函数并返回结果,因为转换操作符将创建一个纯右值,所以你不需要类型是可复制/可移动的:

template<typename F>
struct call_wrapper {
    F&& f;

    constexpr operator decltype(auto)() && {
        return static_cast<F&&>(f)();
    }
};
template<typename F>
call_wrapper(F&&) -> call_wrapper<F>;

std::optional<Foo> f2;
f2.emplace(call_wrapper{foo});
0s0u357o

0s0u357o2#

我们可以利用transform(C++23中的新功能),它支持有保证的省略:

auto f = std::optional(1).transform([](int) { return foo(); });

这比call_wrapper稍微通用一些,因为它甚至在Foo可以从call_wrapper构造的情况下也能工作(因为,例如,它有一个接受所有内容的转换构造函数模板)。

q1qsirdb

q1qsirdb3#

不确定这是否符合您不修改Foofoo的要求,但是您可以使用Bar Package 器,该 Package 器默认通过调用foo来初始化Foo成员:

#include <optional>

namespace {
    struct Foo {
        Foo() = default;
        Foo(Foo const&) = delete;
        Foo(Foo&&) = delete;
        Foo& operator=(Foo const&) = delete;
        Foo& operator=(Foo&&) = delete;
        ~Foo() {}
    };
    Foo foo() { return {}; }

    struct Bar {
        Foo f = foo();
    };
}

int main() {
    std::optional<Bar> f3;
    f3.emplace();
}

我认为标题的答案是否定的。您不能将Foo复制或移动到optional中,但通过 Package 器您可以在适当的位置构建它。

相关问题