Home > Enterprise >  Can I copy-elide an immovable & uncopyable function result into an optional?
Can I copy-elide an immovable & uncopyable function result into an optional?

Time:01-10

I want to store a non-trivial type that is immovable and not copyable in an std::optional. However the object is constructed by a free function. (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();

Without changing Foo or foo();

Thanks to copy elision, I can already do this:

Foo f1 = foo();

And this also compiles, because std::optional only requires the stored type to be destructible:

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

But I cannot fill f2 with a function result:

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

Obviously, because this would require copies or moves of Foo. This is likely impossible, but have I overlooked something?

CodePudding user response:

You can make a class where a conversion operator calls the function and returns the result. Since the conversion operator will create a prvalue, you don't need the type to be copyable/movable:

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

CodePudding user response:

Not sure if this fits your requirement of not modifying Foo or foo, but you can use a Bar wrapper that default initializes a Foo member by calling 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();
}

I think the answer to the title is No. You cannot copy or move a Foo into an optional, but via the wrapper you can construct it in place.

  • Related