I have code like the following that accepts a std::aligned_storage_t
parameter by forwarding reference and is supposed to reinterpret_cast
that into another type and return it to the user.
template <typename AlignedStorageType, typename TypeToReturnAs>
decltype(auto) forward_as_another_type(AlignedStorageType&& storage) {
return *reinterpret_cast<TypeToReturnAs*>(&storage);
}
Is there a good way to maintain the type of reference that storage
is in the return type? For example if storage was an rvalue reference then I would want the return type to also be an rvalue reference.
First, flip the template parameters. You want AlignedStorageType
to be deduced and the other to be explicitly specified:
template <typename TypeToReturnAs, typename AlignedStorageType>
decltype(auto) forward_as_another_type(AlignedStorageType&& storage) {
return *reinterpret_cast<TypeToReturnAs*>(&storage);
}
Next, what you basically want is to conditionally cast the expression. If AlignedStorageType&&
is an X&&
, you want to cast it to a TypeToReturnAs&&
. If it's an X&
, as TypeToReturnAs&
. If an X const&
, as TypeToReturnAs const&
.
We can add a type trait to just match the reference:
template <class T, class U> struct match_reference;
template <class T, class U> struct match_reference<T&, U> { using type = U&; };
template <class T, class U> struct match_reference<T const&, U> { using type = U const&; };
template <class T, class U> struct match_reference<T&&, U> { using type = U&&; };
template <class T, class U> using match_reference_t = typename match_reference<T,U>::type;
And then:
template <typename TypeToReturnAs, typename AlignedStorageType>
decltype(auto) forward_as_another_type(AlignedStorageType&& storage) {
using R = match_reference_t<AlignedStorageType&&, TypeToReturnAs>;
return static_cast<R>(*reinterpret_cast<TypeToReturnAs*>(&storage));
}
Alternatively, if you're only using this as a one-off, you can just write that logic as a conditional:
template <typename TypeToReturnAs, typename AlignedStorageType>
decltype(auto) forward_as_another_type(AlignedStorageType&& storage) {
using R = std::conditional_t<
std::is_lvalue_reference<AlignedStorageType>::value,
TypeToReturnAs&,
TypeToReturnAs&&>;
return static_cast<R>(*reinterpret_cast<TypeToReturnAs*>(&storage));
}
or:
using R = std::conditional_t<
std::is_lvalue_reference<AlignedStorageType>::value,
TypeToReturnAs&,
TypeToReturnAs>;
return std::forward<R>(*reinterpret_cast<TypeToReturnAs*>(&storage));