如何在T为仅移动类型的std::expected<T,E>中使用.value()?
仅移动类型与std::expected配合使用的问题
问题背景
尝试将仅移动类型(拷贝构造函数被删除)与std::expected配合使用时编译失败,报错提示调用了已删除的拷贝构造函数。需要确认仅移动类型是否可用于std::expected,并修改代码使其正常运行(不定义拷贝构造函数)。
错误代码
#include <chrono> #include <expected> #include <stdexcept> #include <string> #include <thread> #include <utility> using namespace std::literals; class Resource { Resource() { /* some setup, might raise */ } public: static std::expected<Resource, std::string> tryCreate() noexcept { try { return Resource(); } catch (const std::runtime_error& exc) { return std::unexpected(exc.what()); } } static const Resource create() noexcept { while (true) { const auto resource{Resource::tryCreate()}; if (resource.has_value()) return resource.value(); // <-- 错误位置 else std::this_thread::sleep_for(1s); } } Resource(const Resource&) = delete; Resource(Resource&& other) noexcept {} Resource& operator=(const Resource&) = delete; Resource& operator=(Resource&& other) noexcept { return *this; } ~Resource() { /* some cleanup */ } void use() const noexcept {} }; int main() noexcept { const auto resource{Resource::create()}; resource.use(); }
报错信息
<source>:25:42: error: call to deleted constructor of 'const Resource' 25 | if (resource.has_value()) return resource.value(); | ^~~~~~~~~~~~~~~~ <source>:29:3: note: 'Resource' has been explicitly marked deleted here 29 | Resource(const Resource&) = delete; | ^ 1 error generated. Compiler returned: 1
解答
仅移动类型能否与std::expected配合使用?
可以。std::expected完全支持仅移动类型作为其值类型,标准中并未限制这一点。问题出在代码中错误的取值方式,而非std::expected的兼容性。
错误原因分析
create()函数中,resource是const std::expected<Resource, std::string>类型,调用resource.value()会返回const Resource&。当你尝试将这个左值引用作为const Resource返回时,编译器会尝试调用拷贝构造函数创建返回值,但Resource的拷贝构造函数已被删除,因此报错。
修改方案
需要将std::expected中的仅移动类型移动出来,而非拷贝。只需修改错误行的取值方式,将resource转为右值,让value()返回右值引用,从而触发移动构造:
// 原错误行 // if (resource.has_value()) return resource.value(); // 修改为 if (resource.has_value()) return std::move(resource).value();
修改后的完整代码
#include <chrono> #include <expected> #include <stdexcept> #include <string> #include <thread> #include <utility> using namespace std::literals; class Resource { Resource() { /* some setup, might raise */ } public: static std::expected<Resource, std::string> tryCreate() noexcept { try { return Resource(); } catch (const std::runtime_error& exc) { return std::unexpected(exc.what()); } } static const Resource create() noexcept { while (true) { const auto resource{Resource::tryCreate()}; if (resource.has_value()) return std::move(resource).value(); // 已修改 else std::this_thread::sleep_for(1s); } } Resource(const Resource&) = delete; Resource(Resource&& other) noexcept {} Resource& operator=(const Resource&) = delete; Resource& operator=(Resource&& other) noexcept { return *this; } ~Resource() { /* some cleanup */ } void use() const noexcept {} }; int main() noexcept { const auto resource{Resource::create()}; resource.use(); }
补充说明
std::move(resource)将const std::expected转为右值,此时调用value()会返回Resource&&(右值引用),可以直接用来构造返回的const Resource(移动构造函数接受右值引用,与返回值的const修饰无关)。- 也可以用
return std::move(*resource);替代,效果一致,因为resource作为std::expected的实例,operator*()在右值情况下会返回右值引用。
内容的提问来源于stack exchange,提问作者Adam Barnes
相关产品推荐
相关产品推荐

