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

Posted

技术标签:

【中文标题】部分专业化消歧优先链的更好模式?【英文标题】:Better pattern for partial specialization disambiguation precedence chain? 【发布时间】:2016-08-07 22:32:46 【问题描述】:

考虑以下一系列偏特化:

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

【问题讨论】:

【参考方案1】:

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

#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

附言。 请记住,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 这比template &lt;typename T&gt; using condition1_t = std::integral_constant&lt;bool, sizeof(T)==4 &amp;&amp; condition0_t&lt;T&gt;::value&gt;; 之类更简单的东西有什么好处(然后类似地定义condition_2,但引用condition_1 而不是condition_0 @DavidHollman 实际上你不能只使用一个 using 来做到这一点,因为它包含最后一个条件的未否定部分值......例如condition1_t 包含sizeof(T)==4,如果应该适合在condition2_t 中使用,它应该是sizeof(T)!=4... @DavidHollman 当然你可以把它分成两个用途,但它真的比那种方法更简单吗? @WojciechFrohmberg 当然。我的布尔逻辑思维放屁【参考方案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>  ;

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

更多花里胡哨

添加启用用户部分特化的能力比库的优先级更高,但原理是一样的。完整版在此demo。

【讨论】:

您的解决方案很有趣,您可能还想看看我编辑的方法。它使用专业化和标签调度类型...... @WojciechFrohmberg 谢谢。我认为无论哪种方法“更好”(对于更好的一些定义),在这里都有两种方法是有用的。感谢您为回答这个问题付出的时间和精力

以上是关于部分专业化消歧优先链的更好模式?的主要内容,如果未能解决你的问题,请参考以下文章

C ++模板专业化优先级。模板化专业化

会计专业考什么证书对职业发展更好!

C++ 部分模板专业化 - 设计简化

VC++专业版与企业版有啥本质区别? 对于以后的使用哪种更好?更方便?

专业实习日志10

[创业之路-58] :公司老板专业性,优先级如何排序?