Как мне использовать std :: under_type в функции-шаблоне, только если тип ввода - класс enum? - PullRequest
2 голосов
/ 13 июля 2020

У меня есть фрагмент кода, который возвращает значение некоторых бит данного числа (я тоже считал классы перечисления как число, используя static_cast).

template<typename Type>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination)
{
    if (offset + n> sizeof(Type) * 8)
        return false;

    Type bitmask = 0;

    for (int i = 0; i < n; ++i)
        bitmask |= (1 << i);

    *destination = static_cast<Type>(input >> offset & bitmask);
    return true;
}

Эта функция пытается вернуть значение n битов input, начиная с offset. Он отлично работает для интегральных типов, но когда я пытаюсь использовать его с классами перечисления, компиляция не выполняется. Я попытался использовать std :: lower_type , и тогда он работал для классов перечисления, но не для целочисленных типов: |. Как я могу использовать это для них обоих? Я имею в виду, что если тип является классом enum, я хочу привести ввод к его underlying_type, выполнить несколько побитовых операций, а затем сохранить результат (используя static_cast) в месте назначения. Но эти приведения не должны выполняться для целочисленных типов, у которых нет under_type.

Ответы [ 2 ]

5 голосов
/ 13 июля 2020

Если нужно часто, можно записать адаптер поверх std::underlying_type. Примерно так

namespace detail {
  template<typename T, bool = std::is_enum<T>::value>
  struct underlying_type { using type = T; };

  template<typename T>
  struct underlying_type<T, true> : ::std::underlying_type<T> {};
}

Когда вы используете detail::underlying_type<T>, подстановка также происходит в аргументе по умолчанию. Когда предоставленный тип не является перечислением, первичный шаблон соответствует аргументам (неявно), а открытый type равен T.

Когда предоставленный тип является перечислением (и только тогда), специализация привыкает. И все, что он делает, - это переход к стандартному признаку.

4 голосов
/ 13 июля 2020

Вы можете использовать SFINAE для разделения реализации класса перечисления:

template <typename Type,
          typename = std::enable_if_t<std::is_enum_v<Type>>>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination) {
    // enum-class implementation
}

и интегральной реализации:

template <typename Type,
          typename = std::enable_if_t<std::is_integral_v<Type>>>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination) {
    // integral implementation
}

Выше приведена версия C ++ 17.

Для C ++ 11 будет что-то вроде:

template <typename Type,
          typename std::enable_if<std::is_enum<Type>::value>::type* = nullptr>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination) {
    // enum-class implementation
}

template <typename Type,
          typename std::enable_if<std::is_integral<Type>::value>::type* = nullptr>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination) {
    // integral implementation
}
...