bit*_*ask 28 c++ copy-elision stdoptional
我想存储一个不平凡的类型,它是不可移动且不可复制的std::optional。然而该对象是由自由函数构造的。(例子)
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();
Run Code Online (Sandbox Code Playgroud)
无需改变Foo或foo();
感谢复制省略,我已经可以做到这一点:
Foo f1 = foo();
Run Code Online (Sandbox Code Playgroud)
这也可以编译,因为std::optional只要求存储的类型是可破坏的:
std::optional<Foo> f2;
f2.emplace();
Run Code Online (Sandbox Code Playgroud)
但我无法填写f2函数结果:
f2 = foo(); // no
f2.emplace(foo()); // no
Run Code Online (Sandbox Code Playgroud)
显然,因为这需要复制或移动Foo. 这可能是不可能的,但我是否忽略了一些事情?
Art*_*yer 30
您可以创建一个类,其中转换运算符调用函数并返回结果。由于转换运算符将创建纯右值,因此您不需要该类型是可复制/可移动的:
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});
Run Code Online (Sandbox Code Playgroud)
T.C*_*.C. 15
我们可以利用transform(C++23 中的新功能),它支持保证省略:
auto f = std::optional(1).transform([](int) { return foo(); });
Run Code Online (Sandbox Code Playgroud)
这比它稍微通用一些,call_wrapper因为它甚至可以在Foo可以构造的情况下工作call_wrapper(因为,例如,它有一个接受所有内容的转换构造函数模板)。
不确定这是否符合您不修改Fooor的要求foo,但您可以使用Bar默认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();
}
Run Code Online (Sandbox Code Playgroud)
我认为标题的答案是否定的。您不能将 a 复制或移动Foo到 an 中optional,但通过包装器您可以就地构建它。