在 C++ 中,我可以将某些内容从 std::variant 中移出吗?

Ori*_*rds 9 c++ stl std-variant

我有一个不可复制但可移动的资源包装类。像这样的东西(伪代码)

class Wrapper {
  SomeResource* m_handle = nullptr;
public:
  Wrapper(const Wrapper&) = delete;
  Wrapper& operator=(const Wrapper&) = delete;

  Wrapper(SomeResource* handle) : m_handle(handle) { }
  Wrapper(Wrapper&& other) {
    std::swap(m_handle, other.m_handle);
  }
}
Run Code Online (Sandbox Code Playgroud)

这一切都很好,但是我有很多这些,并且我有一个函数可以解析一些数据并返回包装器或替代包装器。我想到用std::variant返回值来表达这一点。例如

std::variant<Wrapper, AlternativeWrapper> LoadData(void* bytes, size_t len)
{ ... }
Run Code Online (Sandbox Code Playgroud)

我可以编写这个函数,并且它全部可以编译。即,我在 LoadData 函数内构造一个 Wrapper,然后我可以将其移动到随后返回的变体中。

但另一方面,当我想获取值时,出现此错误(MSVC2019)

error C2280: 'Wrapper::Wrapper(const Wrapper&)': attempting to reference a deleted function
Run Code Online (Sandbox Code Playgroud)

我的代码看起来像这样。

auto result = LoadData(bytes, len);
std::get<Wrapper>(result);
Run Code Online (Sandbox Code Playgroud)

这是有道理的,因为结果仍然存在,但是我如何访问它呢?

Ori*_*rds 8

哎呀,我在发布问题后两分钟就解决了这个问题。

您可以将std::move变体放入std::get.

auto v = LoadData(bytes, len);
std::get<Wrapper>(std::move(v));
Run Code Online (Sandbox Code Playgroud)

或者只是内联它,以便变体成为临时右值

std::get<Wrapper>(LoadData(bytes, len));
Run Code Online (Sandbox Code Playgroud)

[更新:]我最初说过 std::move 破坏了原始变体,但被正确地纠正为它没有......但是发生的情况是变体内部的包装器在移出时被破坏,这正如预期的那样

  • “*这会破坏原始变体*”:不会。它只是返回对元素的右值引用,而不是左值引用。它对变体没有任何作用。 (5认同)