I am writing a variant class(yes i know about std::variant, its just for fun), and this is what i have so far
template<typename First, typename... Rest>
union Variant<First, Rest...>
{
template<size_t N>
using Types = typename Type<N, First, Rest...>::_Type;
First first;
Variant<Rest...> rest;
uint16_t activeIndex;
template<size_t N>
Types<N>& get()
{
}
};
the Type struct just allows me to find the type of the n-th element. I know that all the union's members are at the same memory address so can i just
return *(Types<N>*)&first
in the get function? is this safe? or is there some other way to do this. any help would be appreciated.
CodePudding user response:
What you essentially want to know is if the operation:
*(Types<N>*)&first
adheres to strict aliasing. The answer is yes, so long as the type returned by Types<N>
is a compatible type:
The types T and U are compatible, if they are the same type (same name or aliases introduced by a typedef)
In your example i am guessing that this is passed onto the user in some way by saying that the value must be set and retrieved using the same type. If this is the case and the user adheres to this rule, then it is safe.
If you want to go above and beyond, you can look at the implementation of std::variant
. The std::variant::get
function actually throws if you try to retrieve a value that was not set correctly. To achieve this, you could think of using the reverse of Types<N>
to get the index, and store it when setting. Then check that this is the index you are using when you get
, if they are not the same, then throw.
CodePudding user response:
Compared to directly using address type casting, another way is to use recursion.
In order to reduce the depth of recursion, you can use multiple if constexpr
branches to stop the recursion early.
template<size_t N>
Types<N>& get() const {
if constexpr (N == 0)
return first;
else if constexpr (N == 1)
return rest.first;
else if constexpr (N == 2)
return rest.rest.first;
else
return rest.rest.rest.template get<N - 3>();
}