Date: Fri, 21 Aug 2020 17:08:23 +0000
> template <class... VariantTypes, class ElementType>
> variant<VariantTypes> variant_from_element(ElementType&& element);
Let's use "alternative" rather than "element" to be consistent with std::holds_alternative and std::variant_alternative[_t]. Also, as there can be many alternatives, it would be a pain (and error-prone) to list them all in the correct order, so I'd rather pass the variant type:
template <class Variant, class Alternative>
Variant& variant_from_alternative(Alternative& a);
(and similar for const& and &&). The implementation would then check that Variant is in fact std::variant containing Alternative. Then one can avoid listing alternatives:
using TreeNode=variant<100 alternatives>;
> auto& var = variant_from_element<int, string>(my_arg);
This is something like what I had in mind when saying that "one can theoretically make this mistake and still get the code compiled" - note that by using auto the programmer explicitly and willingly forfeits type checking. I agree there is a risk of UB, especially in your version of the function when one can list the alternatives in a wrong order - I think my version mitigates this, because (I speculate) in practice the code has only few variant types, and they have typedef'ed names as one would be too lazy to write the variant declaration more than once. With all these mitigations, I believe the risk/benefit ratio is very good.
> variant<int, string, string>, you wouldn't know which string element it is.
This is not a problem - the reference to the variant does not depend on which of two strings is held in the variant, so the function returns the same result.
BTW, I thought about your solution and think there are more problems:
* one in effect passes the same reference twice (with different types) to operator(), which creates redundancy and has runtime penalty, and somehow feels weird (I struggle to explain why :-); in contrast, variant_from_alternative is 0-cost.
* if the variant is passed by rvalue reference, the visitor member functions would presumably be declared as
ret_type operator()(Variant&& v, Alternative&& a);
where v and a have in fact the same address. This calls for trouble as by moving out from one of the parameters you tacitly change the other into an undefined state. The problem still remains if one uses Variant& instead of Variant&. (I guess there is no such flexibility for Alternative&& as one should be consistent with std::visit).
Received on 2020-08-21 12:11:51