In C++, can I move something out of a std::variant?
Asked Answered
F

1

9

I have a resource-wrapper class which is noncopyable, but is movable. Something like this (pseudeocode)

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

This is all well and good, however I have a number of these, and I have a function which parses some data and returns EITHER a Wrapper or an alternative wrapper. I thought of using std::variant to express this in a return value. E.g.

std::variant<Wrapper, AlternativeWrapper> LoadData(void* bytes, size_t len)
{ ... }

I can write this function, and it all compiles. I.e. I construct a Wrapper inside the LoadData function, then I can move it into the variant which is then returned.

But on the other side, when I want to get the value out, get this error (MSVC2019)

error C2280: 'Wrapper::Wrapper(const Wrapper&)': attempting to reference a deleted function

My code looks like this.

auto result = LoadData(bytes, len);
std::get<Wrapper>(result);

That makes sense as the result is still sticking around, but then how do I access it?

Facetiae answered 14/2, 2020 at 1:21 Comment(1)
No copy is made in the code you are showing. Are you using the result of std::get<Wrapper>(result); further?Transponder
F
8

Oops, I worked this out two minutes after posting the question.

You can std::move the variant into std::get.

auto v = LoadData(bytes, len);
std::get<Wrapper>(std::move(v));

Or just inline it so the variant becomes a temporary rvalue

std::get<Wrapper>(LoadData(bytes, len));

[Update:] I'd originally said that std::move destroyed the original variant, but was rightly corrected that it doesn't... however what does happen is the Wrapper inside the variant gets destroyed as it is moved out, which is as expected

Facetiae answered 14/2, 2020 at 1:23 Comment(1)
"This destroys the original variant": It does not. It simply returns an rvalue reference to the element instead of an lvalue one. It doesn't do anything to the variant.Transponder

© 2022 - 2024 — McMap. All rights reserved.