As for void, apparently the reasons I had in my head are not the reasons in real life. I thought it might be because of a destructible requirement on the type, but it turns out there really isn't a good reason why they disallowed it, and that a future standard might allow it.
In any event, there are a multitude of variant implementations that allow all sorts of things depending on the behavior you want. Nothing is forcing you to use the standard library.
I just wonder why it is hard to make a variant-type that works with everything. Well, if the language prevents e.g. reference rebinding, there can't be done much.
But not being able to put _anything_ into a variant severely limits the way it can be used for abstraction. Especially for library authors, because they might not no what their users will pass them. So when they write a generic method, that uses variants under the hood, they would have to "pass down" the restrictions to the user and tell them not to pass e.g. void. Same for the interaction of two libraries.
> I just wonder why it is hard to make a variant-type that works with everything.
Because standard C++ has to work for the general case. It's specifically designed so that more pointed or specific implementations that have other concerns (e.g. supporting C-style arrays or void) can do so, accepting the runtime penalties if desired.
> reference rebinding, there can't be done much.
References are syntactic sugar over pointers at worst, and a means of optimization at best. C++ is a pass-by-value language first and foremost, and goes to great lengths to keep things as optimizable as possible when it comes to standardization.
Again, variant supports pointers just fine. It also supports smart-pointers just fine. There's nothing preventing you from using those.
Remember that C++ has to work across all architectures, platforms, etc. Not everything handles references the exact same. Compilers are afforded many liberties when it comes to them in order to optimize for the target machine.
> But not being able to put _anything_ into a variant severely limits the way it can be used for abstraction.
Aside from `void`, I disagree. Like I said before, you can implement your own variant quite easily if you want those things. There are decent reasons (except for `void`) not to include them in the standard.
> Especially for library authors, because they might not no what their users will pass them.
I'm not so sure I understand what you mean here. Templates tell library authors /exactly/ what will be passed to them.
> So when they write a generic method, that uses variants under the hood, they would have to "pass down" the restrictions to the user and tell them not to pass e.g. void.
They don't have to tell the user anything. The compiler will inform them void is not allowed if a type cannot be compiled.
> Or am I misunderstanding the constraints here?
Yes. Most of what std::variant does in terms of type checking happens at compile time. Unless a program has been modified after compilation (which should never be the case), there's no possible way for the "wrong type" to be passed to a variant at runtime, because the assortment of possible types have been checked at compile time.
---
EDIT: I just realized why `void` may not be included, though I admit it's speculation.
`void` is not allowed as a function argument type; it is not equivalent to e.g. `decltype(nullptr)` and simply is the lack of type.
Therefore, there is no valid specialization of `operator=(const T&)` that would accept a "void type" because there is no way to implicitly invoke `operator=(void)` (you'd have to call, literally, `some_variant_object.operator=()`, which is very un-C++).
The alternative would be to have a method akin to `.set_void()`, and it could only be enabled if `void` was one of the types passed to the template parameter pack - and, if it is the only type passed to the parameter pack, all other `operator=()` overloads would have to be disabled.
This is an incredibly confusing API specification that I can understand if never included in the standard.
Note that, in this case, there'd be a difference between "no value" (null) and a "void value" (not-null), which is overly confusing and, again, very un-C++ (or un-C for that matter).
If this is the rationale, it makes a lot of sense and I agree with it. If I need a variant that supports `void`, I'd probably write my own anyway because there's probably a domain-specific use case.