
Paul Smith wrote:
Even though it's called the "never-empty guarantee", I always thought that variant actually provides a strong exception guarantee (i.e. the variant is not just left unempty, but specifically in the previous state) so it's not directly relevant. Am I wrong?
Yes, I believe you are. The problem is that it's hard to provide the basic guarantee. Here's why. Imagine you are assigning one variant to another, but the contained types do not match. You need to destroy the target type first, then construct a source type into the target variant. This construction can throw. If it does, you have an invalid target, and no way to bring it back into a valid state. Any valid state, not just the original. If the variant did have an implicit empty state, you'd use it, but it doesn't, so you can't.
Anyway, there's no need to outright disallow move in any case. The goal is simply to find a good move target. Automatically assuming that a non-throwing default constructor is a good move target is a heuristic.
It's not a heuristic. It's the actual requirement that makes a type usable for the task. It makes no sense to use another requirement in its place, or let the user fiddle with it.