Конструкция метапрограммирования, которая возвращает базовый тип для перечисления и целое число для итератора - PullRequest
4 голосов
/ 10 июля 2019

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

Например:

enum Enum : short { VALUE1, VALUE2 };

int_type<long>::type  // -> long
int_type<Enum>::type  // -> short

Я пытался это

template< typename Type >
struct int_type {
    using type = typename std::enable_if< std::is_enum<Type>::value, typename std::underlying_type<Type>::type >::type;
};
template< typename Type >
struct int_type {
    using type = typename std::enable_if< std::is_integral<Type>::value, Type >::type;
};

но он жалуется на переопределение структуры.

Я тоже это пробовал, но

template< typename Type >
struct int_type {
    using type = typename std::enable_if< std::is_enum<Type>::value, typename std::underlying_type<Type>::type >::type;
    using type = typename std::enable_if< std::is_integral<Type>::value, Type >::type;
};

но потом жалуется на переопределение члена type.

На этом мои навыки метапрограммирования заканчиваются, кто-нибудь может помочь?

РЕДАКТИРОВАТЬ: Я должен был также упомянуть, что наш проект ограничен C ++ 11.

Ответы [ 3 ]

5 голосов
/ 10 июля 2019

Проблема с вашими попытками состоит в том, что вы определяете одно и то же дважды, и хотя часть его использует enable_if, что не делает все определение вложения отключенным, когда enable_if isn не правда. то есть у вас есть два определения int_type::type, даже если одно (или оба) из этих определений недействительны.

То, что вы ищете, это std::conditional:

template< typename Type >
struct int_type {
    using when_enum
      = std::underlying_type<Type>;
    using when_integral
      = std::enable_if<std::is_integral<Type>::value, Type>;
    using type
        = typename std::conditional< std::is_enum<Type>::value,
            when_enum, when_integral
            >::type::type;
};

Или в C ++ 14 и более поздних версиях:

template< typename Type >
struct int_type {
    using when_enum
      = std::underlying_type<Type>;
    using when_integral
      = std::enable_if<std::is_integral<Type>::value, Type>;
    using type
        = typename std::conditional_t< std::is_enum<Type>::value,
            when_enum, when_integral
            >::type;
};

Но я думаю, что, вероятно, напишу его как шаблон псевдонима:

template< typename Type >
  using int_type = std::conditional_t<
    std::is_enum<Type>::value,
    std::underlying_type<Type>,
    std::enable_if<std::is_integral<Type>::value, Type>>;

И, возможно, еще более упростить его и сделать int_type<T> псевдонимом для фактического типа, так что вам не нужно говорить typename int_type<T>::type:

template< typename Type >
  using int_type = typename std::conditional_t<
    std::is_enum<Type>::value,
    std::underlying_type<Type>,
    std::enable_if<std::is_integral<Type>::value, Type>>::type;

(Примечание. В C ++ 17 вместо is_xxx<Type>::value. Можно использовать std::is_enum_v<Type> и std::is_integral_v.)

3 голосов
/ 10 июля 2019

Вы можете сделать это с частичной специализацией.

// primary template
template< typename Type, typename = void >
struct int_type {
};

// partial specialization for enum types
template< typename Type >
struct int_type <Type, typename std::enable_if< std::is_enum<Type>::value >::type > {
    using type = typename std::underlying_type<Type>::type;
};

// partial specialization for integral types
template< typename Type>
struct int_type <Type, typename std::enable_if< std::is_integral<Type>::value >::type > {
    using type = Type;
};

ЖИТЬ

0 голосов
/ 10 июля 2019
template< typename Type >
struct int_type {
    using type = typename std::conditional< std::is_enum<Type>::value, typename std::underlying_type<Type>::type, Type >::type;
};

сделает свое дело. Возможно связать std :: conditional для выбора различных типов / генерации ошибок.

...