Подтвердить что ты не робот

Типовая специализация в зависимости от типа 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...

Я должен признать, что я застрял прямо сейчас, поэтому я благодарю вас за любую помощь или совет.

4b9b3361

Ответ 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;
}