Типовая специализация в зависимости от типа sizeof
Я хотел бы предоставить шаблонную функцию, которая меняет ее реализацию (- > специализацию) в соответствии с размером шаблона.
Что-то похожее на это (опущенные типы), но без if/elseif:
template<class T>
T byteswap(T & swapIt)
{
if(sizeof(T) == 2)
{
return _byteswap_ushort (swapIt);
}
else if(sizeof(T) == 4)
{
return _byteswap_ulong(swapIt);
}
else if(sizeof(T) == 8)
{
return _byteswap_uint64(swapIt);
}
throw std::exception();
}
Я знаю, что есть много дорог для достижения моей цели, но поскольку я пытаюсь узнать о SFINAE
и type traits
, меня особенно интересуют решения, использующие эти методы, чтобы решить во время компиляции, какую специализацию выбрать и какие вызовы не допускаются.
Возможно, реализация признака класса is_4ByteLong и использование boost:: enable_if...
Я должен признать, что я застрял прямо сейчас, поэтому я благодарю вас за любую помощь или совет.
Ответы
Ответ 1
Вам не нужны стили SFINAE или типа. Валидной специализации шаблона достаточно. Конечно, он должен быть специализирован на структурах, поскольку С++ (98) не поддерживает частичную специализацию шаблона функции.
template <typename T, size_t n>
struct ByteswapImpl
/*
{
T operator()(T& swapIt) const { throw std::exception(); }
}
*/ // remove the comments if you need run-time error instead of compile-time error.
;
template <typename T>
struct ByteswapImpl<T, 2> {
T operator()(T& swapIt) const { return _byteswap_ushort (swapIt); }
};
// ...
template <typename T>
T byteswap(T& swapIt) { return ByteswapImpl<T, sizeof(T)>()(swapIt); }
Ответ 2
Просто создайте вспомогательный класс, который принимает размер как аргумент шаблона:
#include <cstddef>
#include <iostream>
template<std::size_t Size>
struct ByteSwapper { };
template<>
struct ByteSwapper<2> {
static unsigned short swap(unsigned short a) {
return 2 * a;
}
};
template<typename T>
T byteswap(const T& a) {
return ByteSwapper<sizeof(T)>::swap(a);
}
int main() {
unsigned short s = 5;
std::cout << byteswap(s) << std::endl;
unsigned int i = 7;
// std::cout << byteswap(i) << std::endl; // error
}
Ответ 3
Просто для демонстрации enable_if
в действии, поскольку вы говорили об этом:
template <class T>
typename boost::enable_if_c< sizeof(T) == 2, T >::type
swapIt(T& rhs) { return _byteswap_short(rhs); }
template <class T>
typename boost::enable_if_c< sizeof(T) == 4, T >::type
swapIt(T& rhs) { return _byteswap_long(rhs); }
и т.д...
И, конечно же, вместо того, чтобы метать, просто нет реализации, если тип не отвечает ни одному из требований и, следовательно, у вас есть ошибка времени компиляции.
Две заметки:
- Использование
typename
и ::type
обязательны
- Я использовал
enable_if_c
, потому что мое выражение напрямую вычисляет логическое значение, тогда как enable_if
требуется тип, содержащий член ::value
, который является логическим.
Ответ 4
Я могу предложить следующий метод:
Его преимущество в том, что вам не нужно throw
исключение, если операнд не имеет допустимого размера. Он просто не свяжет. Чтобы вы проверяли ошибку во время сборки.
template<int size>
void byteswapInPlace(void* p);
template<> void byteswapInPlace<1>(void* p) { /* do nothing */ }
template<> void byteswapInPlace<2>(void* p)
{
_byteswap_ushort((ushort*) p);
}
template<> void byteswapInPlace<4>(void* p)
{
_byteswap_ulong((ulong*) p);
}
template<> void byteswapInPlace<8>(void* p)
{
_byteswap_uint64((uint64*) p);
}
template<class T>
T byteswap(T & swapIt)
{
byteswapInPlace<sizeof(T)>(&swapIt);
return swapIt;
}