You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 17:52:27