部分专业化消歧优先链的更好模式?
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...
我经常看到这种情况(实际上,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.
考虑以下系列的偏特化:
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...
我经常看到这种情况(实际上,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.