组合多个类模板专用化

Combine multiple class template specializations

本文关键字:专用 组合      更新时间:2023-10-16

给定下面的示例,压缩模板专用化的最佳方法是什么,以便一两条指令足以定义所有特殊值?也许是变种?

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,许多潜在的专业,与数字或素数无关;这只是一个简单的例子。

这些类似的问题似乎并不能解决手头的问题(除非我遗漏了什么(:

  • 具有多个可变参数模板的模板专用化
  • 组类模板专业化
  • SFINAE 和部分类模板专业化

只是为了取笑可变参数模板,我提出了几个解决方案。

它们都基于一个constexpr函数,该函数表示值是否在模板可变参数列表中(如 aschepler 的答案中所value_in_list,但这也适用于 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;
}

第一个与 aschepler 的解决方案 (+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, "!" );
}

你在寻找这样的东西吗?

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;
};