简体   繁体   中英

Function template overload resolution

I wanted to write some template functions to handle type punning in a defined way, so I came up with these two functions. The first takes an object and converts it with punning to another. It ensures that both types are POD and of equal size. The second was intended to simply take any pointer (as if void*) but still check to make sure that the pointer was to a POD type. The problem I ran into is if I pass a non-const pointer then the first function will get used instead. What would be the best way of handling this?

template <class TO, class FROM>
FORCE_INLINE TO punning_cast(const FROM &input)
{
    static_assert(std::is_pod<TO>::value, "TO must be POD");
    static_assert(std::is_pod<FROM>::value, "FROM must be POD");
    static_assert(sizeof(TO) == sizeof(FROM), "TO and FROM must be the same size");

    TO out;
    std::memcpy(&out, &input, sizeof(TO));
    return out;
}

template <class TO, class FROM>
FORCE_INLINE TO punning_cast(const FROM *input)
{
    static_assert(std::is_pod<TO>::value, "TO must be POD");
    static_assert(std::is_pod<FROM>::value, "FROM must be POD");

    TO out;
    std::memcpy(&out, input, sizeof(TO));
    return out;
}

Function templates mix weirdly with overloading .

A possible solution (not the unique) would be to use enable_if for the declaration of each function enalbing the second for pointer types and vise versa for the first

#include <type_traits>

template <class TO, class FROM>
FORCE_INLINE typename enable_if<!is_pointer<FROM>::value, TO>::type
    punning_cast(const FROM &input) { ... }

template <class TO, class FROM>
FORCE_INLINE typename enable_if<is_pointer<FROM>::value, TO>::type
    punning_cast(const FROM input) { ... }

So an example of achieving the dissambiguation (between reference and pointer) would be this one

Disclaimer: I like Nikos Athanasiou's answer better for C++11 and onward.

One solution to this (common) problem is to wrap the functions in structs and add a helper function that selects one of them:

template <class TO, class FROM>
struct punning_cast_impl
{
    static FORCE_INLINE TO cast(const FROM &input)
    {
        static_assert(std::is_pod<TO>::value, "TO must be POD");
        static_assert(std::is_pod<FROM>::value, "FROM must be POD");
        static_assert(sizeof(TO) == sizeof(FROM), "TO and FROM must be the same size");

        TO out;
        std::memcpy(&out, &input, sizeof(TO));
        return out;
    }
};

template <class TO, class FROM>
struct punning_cast_impl<TO, FROM*>
{
    static FORCE_INLINE TO cast(const FROM *input)
    {
        static_assert(std::is_pod<TO>::value, "TO must be POD");
        static_assert(std::is_pod<FROM>::value, "FROM must be POD");

        TO out;
        std::memcpy(&out, input, sizeof(TO));
        return out;
    }
};

template<class TO, class FROM>
TO FORCE_INLINE punning_cast(const FROM& input)
{
    return punning_cast_impl<TO, FROM>::cast(input);
}

int main()
{
    double d1 = 50.0;
    int64_t i1 = punning_cast<int64_t>(d1); // calls version #1

    double d2 = 100.0;
    int64_t i2 = punning_cast<int64_t>(&d2); // calls version #2
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM