@timkin1

Как можно сделать специализацию класса для нескольких типов?

Здравствуйте!
Подскажите, пожалуйста, как реализовать специализацию шаблона для определенного набора типов переменных? Класс имеет только один параметризуемый из шаблона тип. Как можно сделать следующее: если вызвать A<int>, A<long>, то будет использоваться одна и та же специализация, а если A<Пользовательский тип> (Заранее как называется пользовательский тип неизвестно), то другая? Просто не хочется для каждого стандартного типа расписывать отдельную специализацию, имеющую абсолютно идентичный код с другими.

Заранее спасибо!
  • Вопрос задан
  • 159 просмотров
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Предположим, есть общий шаблон сериализации данных для работы с абстрактными потоками ввода-вывода.
Обобщенный шаблон такой сущности может выглядеть вот так.
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::vector
template< 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::pair
template< 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;
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Этот прием называется SFINAE.
Repl.it

Пример
#include <iostream>
#include <type_traits>
#include <string>

template <class T, class Enable = void>
class Foo {};

template<class T>
class Foo<T, typename std::enable_if<std::is_integral<T>::value>::type> {
  public:
  std::string wow() {
    return "WOW";
  }
};

template<class T>
class Foo<T, typename std::enable_if<std::is_class<T>::value>::type> {
  public:
  std::string wow() {
    return "Meo";
  }
};

class Bar {};

int main() {
  std::cout << Foo<int>().wow() << '\n';
  std::cout << Foo<long>().wow() << '\n';
  std::cout << Foo<Bar>().wow();

  return 0;
}

WOW
WOW
Meo

Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы