部分专业化消歧优先链的更好模式
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...
实时演示
我经常看到这种事情(事实上,其他地方的另一个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> { };
上一个答案中的辅助结构中包含的优先级链是以继承方式编码的。
更多的铃声和口哨声
添加以比库更高的优先级启用用户部分专业化的功能需要做更多的工作,但原理是一样的。这个演示中的完整版本。
相关文章:
- C++:将控制台输出存储在宏中更好吗
- FFmpeg:制作一个应用程序比直接使用ffmepg更好吗
- 初始化具有非默认构造函数的std::数组项的更好方法
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 为什么新的随机库比std::rand()更好
- 寻找一种更好的方法来表示无符号字符数组
- 哪种方法更好,性能明智
- 什么更好?返回对象指针列表?或返回指向对象列表的指针?
- 什么是更好的做法?通过指针或标识符传递类成员?
- 寻求更好地理解标准::访问
- 线程消息传递或更好:在"大师班"中访问其他班级的成员
- 找到数据集C 模式的更好方法
- 部分专业化消歧优先链的更好模式
- 如何更好地实现我的C++设计模式
- 比工厂更好的设计模式
- 显示列表在使用一次时是否比固定模式更好
- 这感觉有点反模式,有没有更好的方法(C++ - 分配给引用)
- 寻找字符串模式的更好解决方案
- 有没有比迭代更好的方法在C++中执行URL模式匹配
- 更好的设计模式用于读取其他进程内存