部分专业化消歧优先链的更好模式?

Better pattern for partial specialization disambiguation precedence chain?

考虑以下系列的偏特化:

template <typename T, typename Enable=void>
struct foo {
  void operator()() const { cout << "unspecialized" << endl; }
};

template <typename T>
struct foo<T, enable_if_t<
  is_integral<T>::value
>>{
  void operator()() const { cout << "is_integral" << endl; }
};

template <typename T>
struct foo<T, enable_if_t<
  sizeof(T) == 4
    and not is_integral<T>::value
>>{
  void operator()() const { cout << "size 4" << endl; }
};

template <typename T>
struct foo<T, enable_if_t<
  is_fundamental<T>::value
    and not (sizeof(T) == 4)
    and not is_integral<T>::value
>>{
  void operator()() const { cout << "fundamental" << endl; }
};

// etc...   

Live Demo

我经常看到这种情况(实际上,another Whosebug answer elsewhere 对类似问题给出了相同的模式)。虽然这有效,但这段代码存在一些严重的可维护性问题,并且还排除了,例如,如果上述代码在库中,则优先级更高的用户级部分特化。表达这个想法的更好模式是什么?我觉得必须有一些东西(可能涉及继承和可变模板参数?)可以更清晰和可维护地表达这个想法。 (还假设每个特化都是一个完整的 class 而不是一个简单的仿函数,因此重载函数不能以简单的方式工作)。

条件计数的过度增长可以通过辅助结构来解决:

#include <iostream>
#include <type_traits>

using namespace std;

template <bool ThisCondition, class ParentCondition = void, class = void>
struct condition_resolver {
   static constexpr bool is_condition_resolver = true;
   static constexpr bool parent_condition_v = !ThisCondition;
   static constexpr bool value = ThisCondition;
};

template <bool ThisCondition, class ParentCondition>
struct condition_resolver<ThisCondition, ParentCondition, enable_if_t<ParentCondition::is_condition_resolver> > {
   static constexpr bool is_condition_resolver = true;
   static constexpr bool parent_condition_v = !ThisCondition && ParentCondition::parent_condition_v;
   static constexpr bool value = ThisCondition && ParentCondition::parent_condition_v;
};

template <typename T, typename Enable=void>
struct foo {
  void operator()() const { cout << "unspecialized" << endl; }
};

template <typename T>
struct is_integral_foo: condition_resolver<is_integral<T>::value> { };

template <typename T>
struct foo<T, enable_if_t<is_integral_foo<T>::value>>{
  void operator()() const { cout << "is_integral" << endl; }
};

template <typename T>
struct has_size_four_foo: condition_resolver<sizeof(T) == 4, is_integral_foo<T>> { };

template <typename T>
struct foo<T, enable_if_t< has_size_four_foo<T>::value>>{
  void operator()() const { cout << "size 4" << endl; }
};

template <typename T>
struct is_fundamental_foo: condition_resolver<is_fundamental<T>::value, has_size_four_foo<T>> { };

template <typename T>
struct foo<T, enable_if_t<is_fundamental_foo<T>::value>>{
  void operator()() const { cout << "fundamental" << endl; } 
};

typedef char four_sized[4];

int main() {
   foo<int>()();
   foo<four_sized>()();
   foo<nullptr_t>()();
}

输出:

is_integral
size 4
fundamental

PS。 请记住,void 这也是基本的,这将导致编译器产生警告,认为 sizeof(void) 被认为是...

编辑:

如果您真的需要使用专业化来解决条件过度增长的问题,您可能会感兴趣:

#include <iostream>
#include <type_traits>

using namespace std;

template <class Tag, int Level, class... Args>
struct concrete_condition_resolver;


template <class Tag, int Level, class... Args>
struct condition_resolver;

template <class ConditionResolver>
struct condition_resolver_parent {
   template<class CR = ConditionResolver>
   constexpr enable_if_t<CR::level != 0, bool> operator()(bool parent) {
      return (!parent && static_cast<const ConditionResolver*>(this)->condition && typename ConditionResolver::LevelUp()(true)) ||
             (parent && !static_cast<const ConditionResolver*>(this)->condition && typename ConditionResolver::LevelUp()(true));
   }

   template<class CR = ConditionResolver>
   constexpr enable_if_t<CR::level == 0, bool> operator()(bool parent) {
      return (!parent && static_cast<const ConditionResolver*>(this)->condition) ||
             (parent && !static_cast<const ConditionResolver*>(this)->condition);
   }
};

template <class Tag, int Level, class... Args>
struct condition_resolver: concrete_condition_resolver<Tag, Level, Args...>, condition_resolver_parent<condition_resolver<Tag, Level, Args...>> {
   using LevelUp = condition_resolver<Tag, Level - 1, Args...>;
   using tag = Tag;
   static constexpr int level = Level;
   constexpr condition_resolver() {}
};


struct foo_tag { };

template <class First, class... Args>
struct concrete_condition_resolver<foo_tag, 0, First, Args...> {
   static constexpr bool condition = is_integral<First>::value;
};

template <class First, class... Args>
struct concrete_condition_resolver<foo_tag, 1, First, Args...> {
   static constexpr bool condition = sizeof(First) == 4;
};

template <class First, class... Args>
struct concrete_condition_resolver<foo_tag, 2, First, Args...> {
   static constexpr bool condition = is_fundamental<First>::value;
};

template <typename T, typename = void>
struct foo;

template <typename T>
struct foo<T, enable_if_t<condition_resolver<foo_tag, 0, T>()(false)>>{
  void operator()() const { cout << "is_integral" << endl; }
};

template <typename T>
struct foo<T, enable_if_t<condition_resolver<foo_tag, 1, T>()(false)>>{
  void operator()() const { cout << "size 4" << endl; }
};

template <typename T>
struct foo<T, enable_if_t<condition_resolver<foo_tag, 2, T>()(false)>>{
  void operator()() const { cout << "is_fundamental" << endl; }
};


typedef char four_sized[4];

int main() {
   foo<int>()();
   foo<four_sized>()();
   foo<nullptr_t>()();
}

这种方法甚至适用于使用 enable_if 的重载函数,而部分特化仅处理结构...

为什么我要回答我自己的问题

所以自问这个问题以来我就一直被这个问题困扰,而且我对最初的答案从未完全满意。经过多次摆弄和 trial/error,我想出了一个我更喜欢使用标签分派的模式。它实际上是否比以前的答案更好、更具可读性和更可维护性由您来判断,但我更喜欢它。随意拆开它,批评它,打破它。 :-)

基础版

事不宜迟,下面是解决问题最简单版本的代码

template <typename> struct always_true : true_type { };
template <typename> struct always_false : false_type { };

template <typename T, template <class...> class condition=always_false,
  typename flag=integral_constant<bool, condition<T>::value>
>
struct foo;

////////////////////////////////////////
// "unspecialized" version

// put always_true and false_type together here so that no one gets here accidentally
template <typename T, typename true_or_false_type>
struct foo<T, always_true, true_or_false_type> {
  void operator()() const { cout << "unspecialized" << endl; }
};

////////////////////////////////////////
// is_fundamental

template <typename T>
struct foo<T, is_fundamental, true_type> {
  void operator()() const { cout << "is_fundamental" << endl; }
};
template <typename T> struct foo<T, is_fundamental, false_type> : foo<T, always_true> { };

////////////////////////////////////////
// is_integral

template <typename T>
struct foo<T, is_integral, true_type> {
  void operator()() const { cout << "is_integral" << endl; }
};
template <typename T>
struct foo<T, is_integral, false_type> : foo<T, is_fundamental> { };

////////////////////////////////////////
// sizeof(T) == 4

template <typename T>
using size_is_4 = integral_constant<bool, sizeof(T) == 4>;

template <typename T>
struct foo<T, size_is_4, true_type> {
  void operator()() const { cout << "size_is_4" << endl; }
};
template <typename T>
struct foo<T, size_is_4, false_type> : foo<T, is_integral> { };

////////////////////////////////////////
// Now put the most specialized condition in the base of this template

template <typename T, typename true_or_false_type>
struct foo<T, always_false, true_or_false_type> : foo<T, size_is_4> { };

在上一个答案中保存在辅助结构中的优先链是在继承中编码的。

更多花里胡哨的东西

添加启用优先级高于库的用户偏特化的能力需要做更多的工作,但原理是一样的。完整版在此demo.