功能模板重载决议

时间:2014-05-19 19:20:33

标签: c++ templates c++11 type-conversion type-punning

我想编写一些模板函数来以定义的方式处理类型惩罚,所以我想出了这两个函数。第一个采用一个对象并将其与双关语转换为另一个对象。它确保两种类型都是POD并且大小相同。第二个用于简单地获取任何指针(如无效*),但仍检查以确保指针指向POD类型。我遇到的问题是如果我传递一个非const指针,那么将使用第一个函数。处理这个问题的最佳方式是什么?

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;
}

2 个答案:

答案 0 :(得分:3)

功能模板mix weirdly with overloading

一个可能的解决方案(不是唯一的)是使用enable_if来声明每个函数,为第一个指针类型加入第二个

#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) { ... }

所以实现消歧(参考和指针之间)的一个例子是this one

答案 1 :(得分:3)

免责声明:对于C ++ 11及更高版本,我更喜欢Nikos Athanasiou's answer

这个(常见)问题的一个解决方案是将函数包装在结构中并添加一个辅助函数来选择其中一个函数:

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
}