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

Better pattern for partial specialization disambiguation precedence chain?

本文关键字:更好 模式 专业化      更新时间:2023-10-16

考虑以下一系列部分专业化:

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...   

实时演示

我经常看到这种事情(事实上,其他地方的另一个StackOverflow答案对类似的问题给出了相同的模式)。虽然这是可行的,但该代码存在一些严重的可维护性问题,并且还排除了(例如)如果上面的代码在库中,则优先级更高的用户级部分专业化。表达这种想法的更好模式是什么?我觉得必须有一些东西(可能涉及继承和可变模板参数?)可以更干净、更可维护地表达这个想法。(还假设每个专门化都是一个完整的类,而不是一个简单的函子,所以重载函数不会以简单的方式工作)。

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

#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的重载函数,而部分专门化只处理结构。。。

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

所以,自从提出这个问题以来,我一直被这个问题困扰着,我从未对最初的答案完全满意。经过大量的修改和尝试/错误,我想出了一个我更喜欢的模式,它使用标签调度。它是否真的比前面的答案更好、更可读、更可维护,由你来判断,但我更喜欢它。随意地把它拆开,批评它,然后打破它。:-)

基本版本

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

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

上一个答案中的辅助结构中包含的优先级链是以继承方式编码的。

更多的铃声和口哨声

添加以比库更高的优先级启用用户部分专业化的功能需要做更多的工作,但原理是一样的。这个演示中的完整版本。