C++ Logo

std-proposals

Advanced search

Re: std::variant - going from Alternative& to the enclosing variant&

From: Victor Khomenko <victor.khomenko_at_[hidden]>
Date: Fri, 11 Sep 2020 13:35:41 +0000
>One thing, why do you not implement `variant_from_alternative` yourself?

As std::variant was standardised very recently, I used another implementation of a variant back a few years ago. I already had the idea back then, and we briefly discussed adding such a function to that variant implementation with its author. However, we quickly discovered that recursive_wrapper (which that implementation provided) was a real blocker, so dropped the idea. Now, as std::variant does not provide recursive_wrapper, and probably won't in future, I've resurrected the idea.

As Ville already pointed out, one cannot reliably implement this function based on what is in the standard, as some extra assumptions about the internals of the variant are necessary.

For example, it's likely that the offset of a particular alternative type within the variant is a compile-time constant (and even that all alternatives have the same offset - but this stronger assumption is unnecessary), one can construct a constexpr variant containing this alternative, and take the difference between the address of the alternative (that can be obtained using get_if) and the address of the variant, and then subtract this offset when doing a cast. (Care should be taken to work around the issue when the same type occurs multiple times within the variant, as get_if is then ill-formed, even though variant_from_alternative still works.) There is a good chance this will work in many existing implementations, though I never tried. This approach is fragile - I can imagine conformant implementations which break this assumption (e.g. two storages with an extra bit to choose an active storage - to implement strong guarantee for some operations). Moreover, if the offset is not 0, the function will not be 0-cost (although still very cheap).

So it's best if the library vendors implement this function: By placing the storage at 0 offset, they can just do a cast.

> Btw this means this function is cast? And if I use the wrong variant type then
> we will have UB?

This is already explained in the original post - the risk of UB can be mitigated (but not eliminated) by checking that the supplied type is indeed a variant and that it contains the type of the argument as an alternative. Also, the requirement that the programmer must supply the variant type (rather than a list of types of alternatives) helps - the variants are likely to be typedef'ed, so one can just use the type synonym - this mitigates the problem of listing the types in the wrong order.


Received on 2020-09-11 08:39:15