Предположим, есть общий шаблон сериализации данных для работы с абстрактными потоками ввода-вывода.
Обобщенный шаблон такой сущности может выглядеть вот так.
template< typename TValueType >
struct Serializer final
{
static inline const bool Read( const BasicStream& stream, TValueType& value_storage );
static inline const bool Write( BasicStream& stream, const TValueType& value_storage );
};
У этого шаблона может присутствовать набор частных инстанцирований для простых и стандартных типов.
Примерtemplate<>
struct Serializer<int8_t> final
{
static inline const bool Read( const BasicStream& stream, int8_t& value_storage );
static inline const bool Write( BasicStream& stream, const int8_t& value_storage );
};
// ...
template<>
struct Serializer<float> final
{
static inline const bool Read( const BasicStream& stream, float& value_storage );
static inline const bool Write( BasicStream& stream, const float& value_storage );
};
Любой пользователь библиотеки всегда может определить частное инстанцирование сериализатора для своего типа точно в такой же манере. А можно просто отдать свой пользовательский тип на откуп обобщенному шаблону.
А теперь вкусненькое. Среди стандартных типов есть и
std::string
, и
std::vector
, и
std::map
. Если пользователь решит сериализовать весь контейнер своих пользовательских типов, нам не стоит обязывать его писать частное инстанцирование сериализатора для контейнера. Нам надо предусмотреть сериализацию любого стандартного контейнера с любым пользовательским типом.
Мы это делаем с помощью
частичной специализации шаблона сериализатора.
Для произвольной строкиtemplate< typename TCharType, typename TCharTraits, typename TAllocator >
struct Serializer<std::basic_string<TCharType, TCharTraits, TAllocator>> final
{
static inline const bool Read( const BasicStream& stream, std::basic_string<TCharType, TCharTraits, TAllocator>& value_storage );
static inline const bool Write( BasicStream& stream, const std::basic_string<TCharType, TCharTraits, TAllocator>& value_storage );
};
Для std::vectortemplate< typename TElement, typename TAllocator >
struct Serializer<std::vector<TElement, TAllocator>> final
{
static inline const bool Read( const BasicStream& stream, std::vector<TElement, TAllocator>& value_storage );
static inline const bool Write( BasicStream& stream, const std::vector<TElement, TAllocator>& value_storage );
};
Для std::pairtemplate< typename TLeftElement, typename TRightElement >
struct Serializer<std::pair<TLeftElement, TRightElement>> final
{
static inline const bool Read( const BasicStream& stream, std::pair<TLeftElement, TRightElement>& value_storage );
static inline const bool Write( BasicStream& stream, const std::pair<TLeftElement, TRightElement>& value_storage );
};
И так далее.
А вот как может выглядеть внешнее определение функции при частичной специализации.
Пример чтения из стрима в векторtemplate< typename TElement, typename TAllocator >
inline const bool Serializer<std::vector<TElement, TAllocator>>::Read(
const BasicStream& stream,
std::vector<TElement, TAllocator>& value_storage
)
{
size32_t stored_size;
Serializer<size32_t>::Read( stream, stored_size );
value_storage.resize( stored_size );
for( TElement& element : value_storage )
{
Serializer<TElement>::Read( stream, element );
}
return true;
}