std::move doesn't move anything, it casts an object as a movable reference (equivalent to static_cast<T&&>). The compiler doesn't preclude you from accessing the old object (or say anything at all) and there's no guarantee it actually did get moved, it's just a hint. That's not real move semantics, it's a shoddy knock-off.
You also have to manage a new constructor, new reference type and the opt-in because the default remains not using move semantics. Worse yet, there's varying agreement on what you can or should do with the source of a moved value, the STL containers will all continue to let you use the old value, it's just empty now. That's not standard behavior because there is none, and it's de facto now because it's in the STL. What a nightmare. [1]
std::variant is supposed to be associated values on enums, but of course, it doesn't do that either. You don't match, you create a new struct with a bunch of operator() methods on it [or overload a lambda?!] and throw it at std::visit. You can only have one variant of each type. Then it throws exceptions if you start mucking about in ways you shouldn't. There's no context. It's dreadful. [2]
[1] http://yacoder.guru/blog/2015/03/14/cpp-curiosities-std-move...