Search code examples
c++c++11c++14reinterpret-castperfect-forwarding

Forwarding a reference to a class as another type


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.


Solution

  • 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));