Объединение нескольких специализаций шаблонов классов - PullRequest
0 голосов
/ 30 июня 2018

Учитывая приведенный ниже пример, каков наилучший способ сжать специализации шаблона, чтобы одной или двух инструкций было достаточно для определения всех специальных значений? Возможно, Variadics?

enum class PositiveDigit // Not necessarily sequential
{ One=1, Two, Three, Four, Five, Six, Seven, Eight, Nine };

// Base class template
template <std::underlying_type_t<PositiveDigit>>
struct IsNum
{ static constexpr bool aPrimeDigit = false; };

// Specialized class templates to define which positive digits are prime 
// HOW TO BEST COMBINE/CONDENSE THESE? VARIADIC?? Ideally, something like:
//    template <PositiveDigit::Two, PositiveDigit::Three, ……> ……   OR,
//    template <> …… (PositiveDigit::Two, PositiveDigit::Three, ……) ……   OR??
template <>
struct IsNum<static_cast<std::underlying_type_t<PositiveDigit>>(PositiveDigit::Two)>
{ static constexpr bool aPrimeDigit = true; };
template <>
struct IsNum<static_cast<std::underlying_type_t<PositiveDigit>>(PositiveDigit::Three)>
{ static constexpr bool aPrimeDigit = true; };
template <>
struct IsNum<static_cast<std::underlying_type_t<PositiveDigit>>(PositiveDigit::Five)>
{ static constexpr bool aPrimeDigit = true; };
template <>
struct IsNum<static_cast<std::underlying_type_t<PositiveDigit>>(PositiveDigit::Seven)>
{ static constexpr bool aPrimeDigit = true; };

int main() {
    // Note: It's perfectly okay to pass integers beyond the range of the
    //  enum class, they'll simply provide a false result
    IsNum<-5>::aPrimeDigit; // false
    IsNum<13>::aPrimeDigit; // false
    IsNum< 7>::aPrimeDigit; // true!
}

Пожалуйста, примите во внимание, что enum должен оставаться строго напечатанным. Проблема реального мира имеет большую enum class, много потенциальных специализаций и не имеет ничего общего с цифрами или простыми числами; это был простой пример.

Эти похожие вопросы, похоже, не решают проблему (если я что-то упустил):

Ответы [ 2 ]

0 голосов
/ 30 июня 2018

Просто чтобы подшутить над вариативными шаблонами, я предлагаю пару решений.

Оба они основаны на функции constexpr, которая сообщает, находится ли значение в списке переменных шаблона (как value_in_list в ответе aschepler, но это также работает в C ++ 14)

template <typename T, T ... ts>
constexpr bool isInList (T const & t0)
 {
   using unused = bool[];

   bool ret { false };

   (void)unused { false, ret |= t0 == ts... };

   return ret;
 }

Первый из них очень похож на решение Ашеплера (+1) и не использует специализацию шаблонов.

template <std::underlying_type_t<PositiveDigit> I>
struct IsNum1
{ 
   static constexpr bool aPrimeDigit
      = isInList<PositiveDigit, PositiveDigit::Two, PositiveDigit::Three,
                 PositiveDigit::Five, PositiveDigit::Seven>
                    (static_cast<PositiveDigit>(I));
};

Мне кажется, что проще, но если вы действительно (действительно!) Хотите пройти специализацию по шаблонам, вы можете написать что-то вроде следующего:

template <std::underlying_type_t<PositiveDigit>, typename = std::true_type>
struct IsNum2
 { static constexpr bool aPrimeDigit = false; };

template <std::underlying_type_t<PositiveDigit> I>
struct IsNum2<I, std::integral_constant<bool, isInList<
   PositiveDigit, PositiveDigit::Two, PositiveDigit::Three,
   PositiveDigit::Five, PositiveDigit::Seven>
      (static_cast<PositiveDigit>(I))>>
 { static constexpr bool aPrimeDigit = true; };

Ниже приводится полный пример компиляции

#include <type_traits>

enum class PositiveDigit
{ Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine };

template <typename T, T ... ts>
constexpr bool isInList (T const & t0)
 {
   using unused = bool[];

   bool ret { false };

   (void)unused { false, ret |= t0 == ts... };

   return ret;
 }

template <std::underlying_type_t<PositiveDigit> I>
struct IsNum1
{ 
   static constexpr bool aPrimeDigit
      = isInList<PositiveDigit, PositiveDigit::Two, PositiveDigit::Three,
                 PositiveDigit::Five, PositiveDigit::Seven>
                    (static_cast<PositiveDigit>(I));
};

template <std::underlying_type_t<PositiveDigit>, typename = std::true_type>
struct IsNum2
 { static constexpr bool aPrimeDigit = false; };

template <std::underlying_type_t<PositiveDigit> I>
struct IsNum2<I, std::integral_constant<bool, isInList<
   PositiveDigit, PositiveDigit::Two, PositiveDigit::Three,
   PositiveDigit::Five, PositiveDigit::Seven>
      (static_cast<PositiveDigit>(I))>>
 { static constexpr bool aPrimeDigit = true; };

int main ()
 {
   static_assert( false == IsNum1<-5>::aPrimeDigit, "!" );
   static_assert( false == IsNum1<13>::aPrimeDigit, "!" );
   static_assert(  true == IsNum1< 7>::aPrimeDigit, "!" );

   static_assert( false == IsNum2<-5>::aPrimeDigit, "!" );
   static_assert( false == IsNum2<13>::aPrimeDigit, "!" );
   static_assert(  true == IsNum2< 7>::aPrimeDigit, "!" );
 }
0 голосов
/ 30 июня 2018

Вы ищете что-то подобное?

C ++ 17:

#include <type_traits>

template <auto Value, decltype(Value)... List>
struct value_in_list
    : public std::disjunction<std::bool_constant<Value==List>...> {};

template <std::underlying_type_t<PositiveDigit> N>
struct IsNum
{
    static constexpr bool aPrimeDigit =
        value_in_list<static_cast<PositiveDigit>(N),
            PositiveDigit::Two, PositiveDigit::Three,
            PositiveDigit::Five, PositiveDigit::Seven
        >::value;
};

или C ++ 14:

#include <type_traits>

template <typename T, T Value, T... List>
struct value_in_list;

// Base case 1: Value is not in an empty list.
template <typename T, T Value>
struct value_in_list<T, Value> : public std::false_type {};

// Base case 2: Value is in a list that starts with Value.
template <typename T, T Value, T... Rest>
struct value_in_list<T, Value, Value, Rest...>
    : public std::true_type {};

// Recursion case: If a non-empty list does not start with Value,
// Value is in the list if and only if it's in the list with the
// first element removed.
template <typename T, T Value, T First, T... Rest>
struct value_in_list<T, Value, First, Rest...>
    : public value_in_list<T, Value, Rest...> {};

template <std::underlying_type_t<PositiveDigit> N>
struct IsNum
{
    static constexpr bool aPrimeDigit =
        value_in_list<PositiveDigit, static_cast<PositiveDigit>(N),
            PositiveDigit::Two, PositiveDigit::Three,
            PositiveDigit::Five, PositiveDigit::Seven
        >::value;
};
...