Template Specializations with Recursion and std::enable_if

Viewed 89

Suppose I want to serialize the following data in the following way. Some of the data is memcopy serializable, and some is not.

    MemCpySerializable ser;
    NonMemCpySerializable nonser;

    const std::vector<MemCpySerializable> vec1;
    const std::vector<NonMemCpySerializable> vec2;

    FastBinarySerializer<MemCpySerializable>::SerializeValue(ser);
    FastBinarySerializer<NonMemCpySerializable>::SerializeValue(nonser);
      
    FastBinarySerializer<std::vector<MemCpySerializable>>::SerializeValue(vec1);
    FastBinarySerializer<std::vector<NonMemCpySerializable>>::SerializeValue(vec2);

The previously mentioned types are defined in this way:

template<typename T>
struct IsMemcopySerializable
{
    static constexpr bool value = std::is_trivially_copyable<T>::value && std::is_trivially_destructible<T>::value &&
        !std::is_pointer<T>::value;
};

struct MemCpySerializable
{
};

template<>
struct IsMemcopySerializable<MemCpySerializable>
{
    static constexpr bool value = true;
};


struct NonMemCpySerializable
{
};

template<>
struct IsMemcopySerializable<NonMemCpySerializable>
{
    static constexpr bool value = false;
};

As you can see, the only difference in the two struct types is the value in IsMemcopySerializable::value.

Note that IsMemcopySerializablestd::vector<T>::value is false for all T.

We define FastBinarySerializer in the following way:

//Default Template Arguments
struct DefaultType;
struct NonDefaultType; //This is unused, but it may be helpful as a default template argument.

//Primary Template
template<typename T, typename U = DefaultType, typename Enable = void >
struct FastBinarySerializer;

template<typename T, typename U>
struct FastBinarySerializer<
    T,
    U,
    typename std::enable_if<
    IsMemcopySerializable<T>::value>::type>
{
    static void SerializeValue(const T& value) noexcept
    {
        //Memcopy serialization
    }
};

template<typename T, typename U>
struct FastBinarySerializer<
    T,
    U,
    typename std::enable_if<
    !IsMemcopySerializable<T>::value>::type>
{
    static void SerializeValue(const T& value)
    {
        //Fallback case. This will only work for specific types (not std::vector<T>). 
        // We will assume that any nonspecialized type T with !IsMemcopySerializable<T>::value
        // will be handled by this function.
    }

};

template<typename T>
struct FastBinarySerializer<std::vector<T>, DefaultType, void>
{
    static void SerializeValue(const std::vector<T>& value)
    {
        FastBinarySerializer<uint32_t>::SerializeValue(value.size());

        for (auto& item : value)
        {
            FastBinarySerializer<T>::SerializeValue(item);
        }
    }
};

The problem is that the following line generates this error:

    FastBinarySerializer<std::vector<MemCpySerializable>>::SerializeValue(vec1);
      //error C2752: 'FastBinarySerializer<std::vector<MemCpySerializable, 
      //std::allocator<MemCpySerializable>>,DefaultType,void>': more than one partial specialization 
      //matches the template argument list

The issue is that std::vector is not MemcopySerializable. The FastBinarySerializer class template matches two of the specializations. Is there a way that I can make FastBinarySerializerstd::vector<NonMemCpySerializable> match only with the std::vector specialization? I tried to use DefaultType and NonDefaultType to enforce that the std::vector specialization was prioritized to no avail.

My constraints: FastBinarySerializer must remain as a class template and not as a function template. SerializeValue must have the same function signature throughout. I cannot manually specify DefaultType or NonDefault type in the class template instantiation.

0 Answers
Related