部分专业化消歧优先链的更好模式?
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 <typename T> using condition1_t = std::integral_constant<bool, sizeof(T)==4 && condition0_t<T>::value>;
之类更简单的东西有什么好处(然后类似地定义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 谢谢。我认为无论哪种方法“更好”(对于更好的一些定义),在这里都有两种方法是有用的。感谢您为回答这个问题付出的时间和精力以上是关于部分专业化消歧优先链的更好模式?的主要内容,如果未能解决你的问题,请参考以下文章