如何使用SFINAE创建相同方法的2种不同实现

时间:2014-11-04 10:20:03

标签: c++ templates c++11 sfinae

我已经阅读了一些有关SFINAE的文章,但找不到我的案例的解决方案。这就是我想要做的事情:

#include <type_traits>

struct CByteArray {};
struct HLVariant {
    HLVariant() {}
    HLVariant(const HLVariant&) {}
    HLVariant(const CByteArray&) {}

    };

template <typename T>
struct Serializer
{
    static inline typename std::enable_if<std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
    {
        static_assert(std::is_pod<T>::value, "Not a POD type");
        return CByteArray();
    }

    static inline typename std::enable_if<!std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
    {
        return Serializer<HLVariant>::serialize(HLVariant(value));
    }
};

template <>
struct Serializer<HLVariant>
{
    static inline CByteArray serialize(const HLVariant& value)
    {
        return CByteArray();
    }
};

int main()
{
    int i = 0;
    Serializer<int>::serialize(i);
    Serializer<CByteArray>::serialize(CByteArray());
    Serializer<HLVariant>::serialize(HLVariant());

    return 0;
}

但是,当然,我得到了error C2039: 'type' : is not a member of 'std::enable_if<false,CByteArray>'

如何实现我的目标?

此外,是否可以以某种方式重新组织Serializer,以便可以隐式推断模板参数 - Serializer::serialize(i);而不是Serializer<int>::serialize(i);

2 个答案:

答案 0 :(得分:3)

要使用std::enable_if<condition>,您必须处于条件的模板中。一种选择是将您的函数声明为具有默认参数

的模板
template <typename T>
struct Serializer
{
    template<bool pod = std::is_pod<T>::value>  // template over condition
    static typename std::enable_if<pod, CByteArray>::type
    serialize(const T& value)
    { return CByteArray(); }

    template<bool pod = std::is_pod<T>::value>
    static typename std::enable_if<!pod, CByteArray>::type 
    serialize(const T& value)
    { return Serializer<HLVariant>::serialize(HLVariant(value)); }
};

template<>
struct Serializer<HLVariant>
{
    static CByteArray serialize(const HLVariant&);
};

或者,您可以直接在类模板的范围内应用SFINAE:

template<typename T, typename = void> struct Serializer;

template<>
struct Serializer<HLVariant>
{
    static CByteArray serialize(const HLVariant&)
    { return CByteArray(); }
};

template<typename T>
struct Serializer<T,typename std::enable_if<is_pod<T>::type>
{
    static CByteArray serialize(const T&)
    { return CByteArray(); }
};

template<typename T>
struct Serializer<T,typename std::enable_if<!is_pod<T>::type>
{
    static CByteArray serialize(const T&value)
    { return Serializer<HLVariant>::serialize(HLVariant(value));
};

或者你可以摆脱类Serializer并通过模板函数直接声明:

inline CByteArray
serialize(const HLVariant&)
{ return CByteArray(); }

template<typename T>
inline typename enable_if<std::is_pod<T>::value, CByteArray>::type
serialize(const T&)
{ return CByteArray(); }

template<typename T>
inline typename enable_if<!std::is_pod<T>::value, CByteArray>::type
serialize(const T&value)
{ return serialize(HLVariant(value)); }

BTW,C ++ 14定义了非常有用的别名

template<bool C, typename T>
using enable_if_t = typename enable_if<C,T>::type;

但你当然可以这样做。这样可以避免繁琐的typename::type

答案 1 :(得分:3)

SFINAE是&#34;替换失败不是错误的首字母缩写。&#34;根据定义,这意味着它仅在模板参数替换模板定义中的参数时适用。您的serialize函数是类模板的成员函数,它们本身不是函数模板。直接的答案是将函数转换为函数模板(Live code):

template <typename> struct Serializer;

template <>
struct Serializer<HLVariant>
{
    static CByteArray serialize(const HLVariant& /* value */)
    {
        return CByteArray();
    }
};

template <typename T>
struct Serializer
{
    template <typename U = T>
    static typename std::enable_if<std::is_pod<U>::value, CByteArray>::type
    serialize(const U& /* value*/)
    {
        static_assert(std::is_pod<U>::value, "Not a POD type");
        return CByteArray();
    }

    template <typename U = T>
    static typename std::enable_if<!std::is_pod<U>::value, CByteArray>::type
    serialize(const U& value)
    {
        return Serializer<HLVariant>::serialize(HLVariant(value));
    }
};

我删除了冗余的inline,因为类主体中定义的所有函数都是隐式内联的,我重新定位Serializer<HLVariant>特化以确保在引用之前正确声明它。拥有一个只有静态成员函数的类有点傻了;您可以更合理地将其实现为一组重载函数(Live code):

inline CByteArray serialize(const HLVariant& /* value */)
{
    return CByteArray();
}

template <typename T>
inline typename std::enable_if<std::is_pod<T>::value, CByteArray>::type
serialize(const T& /* value*/)
{
    static_assert(std::is_pod<T>::value, "Not a POD type");
    return CByteArray();
}

template <typename T>
inline typename std::enable_if<!std::is_pod<T>::value, CByteArray>::type
serialize(const T& value)
{
    return serialize(HLVariant(value));
}

int main()
{
    int i = 0;
    serialize(i);
    serialize(CByteArray());
    serialize(HLVariant());
}

鉴于SFINAE使用阻碍了代码可读性,我更倾向于在此实例中使用标签分派。不是使用SFINAE管理两个函数的重载解析,而是使用第三个函数调用POD或非POD(Yet more live code)的相应实现:

inline CByteArray serialize(const HLVariant& /* value */)
{
    return CByteArray();
}

template <typename T>
inline CByteArray serialize(std::true_type, const T& /* value*/)
{
    static_assert(std::is_pod<T>::value, "Not a POD type");
    return CByteArray();
}

template <typename T>
inline CByteArray serialize(std::false_type, const T& value)
{
    return serialize(HLVariant(value));
}

template <typename T>
inline CByteArray serialize(const T& value)
{
    return serialize(std::is_pod<T>{}, value);
}

SFINAE功能强大,但又足够危险,可以安全地锁定您可以使用更简单的工具解决的问题。