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

时间:2016-04-15 19:58:08

标签: c++ templates idioms partial-specialization

考虑以下一系列部分特化:

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 StackOverflow answer elsewhere给出了类似问题的相同模式)。虽然这有效,但是如果上述代码在库中,则该代码具有一些严重的可维护性问题,并且还排除了例如具有更高优先级的用户级部分特化。表达这个想法的更好的模式是什么?我觉得必须有一些东西(可能涉及继承和可变参数模板参数?),可以更干净,更可维护地表达这个想法。 (假设每个专业都是一个完整的课程,而不是一个简单的仿函数,所以过载的函数不能以简单的方式工作)。

2 个答案:

答案 0 :(得分:3)

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

#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的重载函数,而部分特化仅适用于结构...

答案 1 :(得分:2)

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

因此,自从提出这个问题以来,我一直被这个问题所困扰,而且我对原始答案从未完全满意。经过多次摆弄和试验/错误后,我想出了一个模式,我更喜欢使用标签调度。它是否真的比以前的答案更好,更易读,更易于维护,供您判断,但我更喜欢它。随意挑选它,批评它,打破它。 : - )

基本版

不用多说,这里是解决最简单版本问题的代码

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

在前一个答案的helper结构中保存的优先级链在继承中编码。

更多铃声和口哨

添加使用优先级高于库的用户部分特化的功能需要多做一些,但原理是相同的。此demo中的完整版。